CIRCT  18.0.0git
LowerIntrinsics.cpp
Go to the documentation of this file.
1 //===- LowerIntrinsics.cpp - Lower Intrinsics -------------------*- C++ -*-===//
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 file defines the LowerIntrinsics pass. This pass processes FIRRTL
10 // extmodules with intrinsic annotations and rewrites the instances as
11 // appropriate.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "PassDetails.h"
22 #include "mlir/IR/Diagnostics.h"
23 #include "mlir/IR/ImplicitLocOpBuilder.h"
24 #include "llvm/ADT/APSInt.h"
25 #include "llvm/ADT/PostOrderIterator.h"
26 #include "llvm/ADT/StringExtras.h"
27 #include "llvm/Support/Debug.h"
28 
29 using namespace circt;
30 using namespace firrtl;
31 
32 // Pass Infrastructure
33 //===----------------------------------------------------------------------===//
34 
35 namespace {
36 struct LowerIntrinsicsPass : public LowerIntrinsicsBase<LowerIntrinsicsPass> {
37  void runOnOperation() override;
38 };
39 } // end anonymous namespace
40 
41 static ParseResult hasNPorts(StringRef name, FModuleLike mod, unsigned n) {
42  if (mod.getPorts().size() != n) {
43  mod.emitError(name) << " has " << mod.getPorts().size()
44  << " ports instead of " << n;
45  return failure();
46  }
47  return success();
48 }
49 
50 static ParseResult namedPort(StringRef name, FModuleLike mod, unsigned n,
51  StringRef portName) {
52  auto ports = mod.getPorts();
53  if (n >= ports.size()) {
54  mod.emitError(name) << " missing port " << n;
55  return failure();
56  }
57  if (!ports[n].getName().equals(portName)) {
58  mod.emitError(name) << " port " << n << " named '" << ports[n].getName()
59  << "' instead of '" << portName << "'";
60  return failure();
61  }
62  return success();
63 }
64 
65 template <typename T>
66 static ParseResult typedPort(StringRef name, FModuleLike mod, unsigned n) {
67  auto ports = mod.getPorts();
68  if (n >= ports.size()) {
69  mod.emitError(name) << " missing port " << n;
70  return failure();
71  }
72  if (!isa<T>(ports[n].type)) {
73  mod.emitError(name) << " port " << n << " not of correct type";
74  return failure();
75  }
76  return success();
77 }
78 
79 template <typename T>
80 static ParseResult sizedPort(StringRef name, FModuleLike mod, unsigned n,
81  int32_t size) {
82  auto ports = mod.getPorts();
83  if (failed(typedPort<T>(name, mod, n)))
84  return failure();
85  if (cast<T>(ports[n].type).getWidth() != size) {
86  mod.emitError(name) << " port " << n << " not size " << size;
87  return failure();
88  }
89  return success();
90 }
91 
92 static ParseResult resetPort(StringRef name, FModuleLike mod, unsigned n) {
93  auto ports = mod.getPorts();
94  if (n >= ports.size()) {
95  mod.emitError(name) << " missing port " << n;
96  return failure();
97  }
98  if (isa<ResetType, AsyncResetType>(ports[n].type))
99  return success();
100  if (auto uintType = dyn_cast<UIntType>(ports[n].type))
101  if (uintType.getWidth() == 1)
102  return success();
103  mod.emitError(name) << " port " << n << " not of correct type";
104  return failure();
105 }
106 
107 static ParseResult hasNParam(StringRef name, FModuleLike mod, unsigned n,
108  unsigned c = 0) {
109  unsigned num = 0;
110  if (mod.getParameters())
111  num = mod.getParameters().size();
112  if (num < n || num > n + c) {
113  auto d = mod.emitError(name) << " has " << num << " parameters instead of ";
114  if (c == 0)
115  d << n;
116  else
117  d << " between " << n << " and " << (n + c);
118  return failure();
119  }
120  return success();
121 }
122 
123 static ParseResult namedParam(StringRef name, FModuleLike mod,
124  StringRef paramName, bool optional = false) {
125  for (auto a : mod.getParameters()) {
126  auto param = cast<ParamDeclAttr>(a);
127  if (param.getName().getValue().equals(paramName)) {
128  if (isa<StringAttr>(param.getValue()))
129  return success();
130 
131  mod.emitError(name) << " has parameter '" << param.getName()
132  << "' which should be a string but is not";
133  return failure();
134  }
135  }
136  if (optional)
137  return success();
138  mod.emitError(name) << " is missing parameter " << paramName;
139  return failure();
140 }
141 
142 static ParseResult namedIntParam(StringRef name, FModuleLike mod,
143  StringRef paramName, bool optional = false) {
144  for (auto a : mod.getParameters()) {
145  auto param = cast<ParamDeclAttr>(a);
146  if (param.getName().getValue().equals(paramName)) {
147  if (isa<IntegerAttr>(param.getValue()))
148  return success();
149 
150  mod.emitError(name) << " has parameter '" << param.getName()
151  << "' which should be an integer but is not";
152  return failure();
153  }
154  }
155  if (optional)
156  return success();
157  mod.emitError(name) << " is missing parameter " << paramName;
158  return failure();
159 }
160 
161 static bool lowerCirctSizeof(InstanceGraph &ig, FModuleLike mod) {
162  auto ports = mod.getPorts();
163  if (hasNPorts("circt.sizeof", mod, 2) ||
164  namedPort("circt.sizeof", mod, 0, "i") ||
165  namedPort("circt.sizeof", mod, 1, "size") ||
166  sizedPort<UIntType>("circt.sizeof", mod, 1, 32) ||
167  hasNParam("circt.sizeof", mod, 0))
168  return false;
169 
170  for (auto *use : ig.lookup(mod)->uses()) {
171  auto inst = cast<InstanceOp>(use->getInstance().getOperation());
172  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
173  auto inputWire = builder.create<WireOp>(ports[0].type).getResult();
174  inst.getResult(0).replaceAllUsesWith(inputWire);
175  auto size = builder.create<SizeOfIntrinsicOp>(inputWire);
176  inst.getResult(1).replaceAllUsesWith(size);
177  inst.erase();
178  }
179  return true;
180 }
181 
182 static bool lowerCirctIsX(InstanceGraph &ig, FModuleLike mod) {
183  auto ports = mod.getPorts();
184  if (hasNPorts("circt.isX", mod, 2) || namedPort("circt.isX", mod, 0, "i") ||
185  namedPort("circt.isX", mod, 1, "found") ||
186  sizedPort<UIntType>("circt.isX", mod, 1, 1) ||
187  hasNParam("circt.isX", mod, 0))
188  return false;
189 
190  for (auto *use : ig.lookup(mod)->uses()) {
191  auto inst = cast<InstanceOp>(use->getInstance().getOperation());
192  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
193  auto inputWire = builder.create<WireOp>(ports[0].type).getResult();
194  inst.getResult(0).replaceAllUsesWith(inputWire);
195  auto size = builder.create<IsXIntrinsicOp>(inputWire);
196  inst.getResult(1).replaceAllUsesWith(size);
197  inst.erase();
198  }
199  return true;
200 }
201 
202 static bool lowerCirctPlusArgTest(InstanceGraph &ig, FModuleLike mod) {
203  if (hasNPorts("circt.plusargs.test", mod, 1) ||
204  namedPort("circt.plusargs.test", mod, 0, "found") ||
205  sizedPort<UIntType>("circt.plusargs.test", mod, 0, 1) ||
206  hasNParam("circt.plusargs.test", mod, 1) ||
207  namedParam("circt.plusargs.test", mod, "FORMAT"))
208  return false;
209 
210  auto param = cast<ParamDeclAttr>(mod.getParameters()[0]);
211  for (auto *use : ig.lookup(mod)->uses()) {
212  auto inst = cast<InstanceOp>(use->getInstance().getOperation());
213  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
214  auto newop = builder.create<PlusArgsTestIntrinsicOp>(
215  cast<StringAttr>(param.getValue()));
216  inst.getResult(0).replaceAllUsesWith(newop);
217  inst.erase();
218  }
219  return true;
220 }
221 
222 static bool lowerCirctPlusArgValue(InstanceGraph &ig, FModuleLike mod) {
223  if (hasNPorts("circt.plusargs.value", mod, 2) ||
224  namedPort("circt.plusargs.value", mod, 0, "found") ||
225  namedPort("circt.plusargs.value", mod, 1, "result") ||
226  sizedPort<UIntType>("circt.plusargs.value", mod, 0, 1) ||
227  hasNParam("circt.plusargs.value", mod, 1) ||
228  namedParam("circt.plusargs.value", mod, "FORMAT"))
229  return false;
230 
231  auto param = cast<ParamDeclAttr>(mod.getParameters()[0]);
232 
233  for (auto *use : ig.lookup(mod)->uses()) {
234  auto inst = cast<InstanceOp>(use->getInstance().getOperation());
235  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
236  auto newop = builder.create<PlusArgsValueIntrinsicOp>(
237  inst.getResultTypes(), cast<StringAttr>(param.getValue()));
238  inst.getResult(0).replaceAllUsesWith(newop.getFound());
239  inst.getResult(1).replaceAllUsesWith(newop.getResult());
240  inst.erase();
241  }
242  return true;
243 }
244 
245 static bool lowerCirctClockGate(InstanceGraph &ig, FModuleLike mod) {
246  if (hasNPorts("circt.clock_gate", mod, 3) ||
247  namedPort("circt.clock_gate", mod, 0, "in") ||
248  namedPort("circt.clock_gate", mod, 1, "en") ||
249  namedPort("circt.clock_gate", mod, 2, "out") ||
250  typedPort<ClockType>("circt.clock_gate", mod, 0) ||
251  sizedPort<UIntType>("circt.clock_gate", mod, 1, 1) ||
252  typedPort<ClockType>("circt.clock_gate", mod, 2) ||
253  hasNParam("circt.clock_gate", mod, 0))
254  return false;
255 
256  for (auto *use : ig.lookup(mod)->uses()) {
257  auto inst = cast<InstanceOp>(use->getInstance().getOperation());
258  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
259  auto in = builder.create<WireOp>(inst.getResult(0).getType()).getResult();
260  auto en = builder.create<WireOp>(inst.getResult(1).getType()).getResult();
261  inst.getResult(0).replaceAllUsesWith(in);
262  inst.getResult(1).replaceAllUsesWith(en);
263  auto out = builder.create<ClockGateIntrinsicOp>(in, en, Value{});
264  inst.getResult(2).replaceAllUsesWith(out);
265  inst.erase();
266  }
267  return true;
268 }
269 
270 template <bool isMux2>
271 static bool lowerCirctMuxCell(InstanceGraph &ig, FModuleLike mod) {
272  StringRef mnemonic = isMux2 ? "circt.mux2cell" : "circt.mux4cell";
273  unsigned portNum = isMux2 ? 4 : 6;
274  if (hasNPorts(mnemonic, mod, portNum) || namedPort(mnemonic, mod, 0, "sel") ||
275  typedPort<UIntType>(mnemonic, mod, 0)) {
276  return false;
277  }
278 
279  if (isMux2) {
280  if (namedPort(mnemonic, mod, 1, "high") ||
281  namedPort(mnemonic, mod, 2, "low") ||
282  namedPort(mnemonic, mod, 3, "out"))
283  return false;
284  } else {
285  if (namedPort(mnemonic, mod, 1, "v3") ||
286  namedPort(mnemonic, mod, 2, "v2") ||
287  namedPort(mnemonic, mod, 3, "v1") ||
288  namedPort(mnemonic, mod, 4, "v0") || namedPort(mnemonic, mod, 5, "out"))
289  return false;
290  }
291 
292  for (auto *use : ig.lookup(mod)->uses()) {
293  auto inst = cast<InstanceOp>(use->getInstance().getOperation());
294  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
295  SmallVector<Value> operands;
296  operands.reserve(portNum - 1);
297  for (unsigned i = 0; i < portNum - 1; i++) {
298  auto v = builder.create<WireOp>(inst.getResult(i).getType()).getResult();
299  operands.push_back(v);
300  inst.getResult(i).replaceAllUsesWith(v);
301  }
302  Value out;
303  if (isMux2)
304  out = builder.create<Mux2CellIntrinsicOp>(operands);
305  else
306  out = builder.create<Mux4CellIntrinsicOp>(operands);
307  inst.getResult(portNum - 1).replaceAllUsesWith(out);
308  inst.erase();
309  }
310 
311  return true;
312 }
313 
314 static bool lowerCirctLTLAnd(InstanceGraph &ig, FModuleLike mod) {
315  if (hasNPorts("circt.ltl.and", mod, 3) ||
316  namedPort("circt.ltl.and", mod, 0, "lhs") ||
317  namedPort("circt.ltl.and", mod, 1, "rhs") ||
318  namedPort("circt.ltl.and", mod, 2, "out") ||
319  sizedPort<UIntType>("circt.ltl.and", mod, 0, 1) ||
320  sizedPort<UIntType>("circt.ltl.and", mod, 1, 1) ||
321  sizedPort<UIntType>("circt.ltl.and", mod, 2, 1) ||
322  hasNParam("circt.ltl.and", mod, 0))
323  return false;
324 
325  for (auto *use : ig.lookup(mod)->uses()) {
326  auto inst = cast<InstanceOp>(use->getInstance().getOperation());
327  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
328  auto lhs = builder.create<WireOp>(inst.getResult(0).getType()).getResult();
329  auto rhs = builder.create<WireOp>(inst.getResult(1).getType()).getResult();
330  inst.getResult(0).replaceAllUsesWith(lhs);
331  inst.getResult(1).replaceAllUsesWith(rhs);
332  auto out = builder.create<LTLAndIntrinsicOp>(lhs.getType(), lhs, rhs);
333  inst.getResult(2).replaceAllUsesWith(out);
334  inst.erase();
335  }
336  return true;
337 }
338 
339 static bool lowerCirctLTLOr(InstanceGraph &ig, FModuleLike mod) {
340  if (hasNPorts("circt.ltl.or", mod, 3) ||
341  namedPort("circt.ltl.or", mod, 0, "lhs") ||
342  namedPort("circt.ltl.or", mod, 1, "rhs") ||
343  namedPort("circt.ltl.or", mod, 2, "out") ||
344  sizedPort<UIntType>("circt.ltl.or", mod, 0, 1) ||
345  sizedPort<UIntType>("circt.ltl.or", mod, 1, 1) ||
346  sizedPort<UIntType>("circt.ltl.or", mod, 2, 1) ||
347  hasNParam("circt.ltl.or", mod, 0))
348  return false;
349 
350  for (auto *use : ig.lookup(mod)->uses()) {
351  auto inst = cast<InstanceOp>(use->getInstance().getOperation());
352  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
353  auto lhs = builder.create<WireOp>(inst.getResult(0).getType()).getResult();
354  auto rhs = builder.create<WireOp>(inst.getResult(1).getType()).getResult();
355  inst.getResult(0).replaceAllUsesWith(lhs);
356  inst.getResult(1).replaceAllUsesWith(rhs);
357  auto out = builder.create<LTLOrIntrinsicOp>(lhs.getType(), lhs, rhs);
358  inst.getResult(2).replaceAllUsesWith(out);
359  inst.erase();
360  }
361  return true;
362 }
363 
364 static bool lowerCirctLTLDelay(InstanceGraph &ig, FModuleLike mod) {
365  if (hasNPorts("circt.ltl.delay", mod, 2) ||
366  namedPort("circt.ltl.delay", mod, 0, "in") ||
367  namedPort("circt.ltl.delay", mod, 1, "out") ||
368  sizedPort<UIntType>("circt.ltl.delay", mod, 0, 1) ||
369  sizedPort<UIntType>("circt.ltl.delay", mod, 1, 1) ||
370  hasNParam("circt.ltl.delay", mod, 1, 2) ||
371  namedIntParam("circt.ltl.delay", mod, "delay") ||
372  namedIntParam("circt.ltl.delay", mod, "length", true))
373  return false;
374 
375  auto getI64Attr = [&](int64_t value) {
376  return IntegerAttr::get(IntegerType::get(mod.getContext(), 64), value);
377  };
378  auto params = mod.getParameters();
379  auto delay = getI64Attr(params[0]
380  .cast<ParamDeclAttr>()
381  .getValue()
382  .cast<IntegerAttr>()
383  .getValue()
384  .getZExtValue());
385  IntegerAttr length;
386  if (params.size() >= 2)
387  if (auto lengthDecl = cast<ParamDeclAttr>(params[1]))
388  length = getI64Attr(
389  cast<IntegerAttr>(lengthDecl.getValue()).getValue().getZExtValue());
390 
391  for (auto *use : ig.lookup(mod)->uses()) {
392  auto inst = cast<InstanceOp>(use->getInstance().getOperation());
393  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
394  auto in = builder.create<WireOp>(inst.getResult(0).getType()).getResult();
395  inst.getResult(0).replaceAllUsesWith(in);
396  auto out =
397  builder.create<LTLDelayIntrinsicOp>(in.getType(), in, delay, length);
398  inst.getResult(1).replaceAllUsesWith(out);
399  inst.erase();
400  }
401  return true;
402 }
403 
404 static bool lowerCirctLTLConcat(InstanceGraph &ig, FModuleLike mod) {
405  if (hasNPorts("circt.ltl.concat", mod, 3) ||
406  namedPort("circt.ltl.concat", mod, 0, "lhs") ||
407  namedPort("circt.ltl.concat", mod, 1, "rhs") ||
408  namedPort("circt.ltl.concat", mod, 2, "out") ||
409  sizedPort<UIntType>("circt.ltl.concat", mod, 0, 1) ||
410  sizedPort<UIntType>("circt.ltl.concat", mod, 1, 1) ||
411  sizedPort<UIntType>("circt.ltl.concat", mod, 2, 1) ||
412  hasNParam("circt.ltl.concat", mod, 0))
413  return false;
414 
415  for (auto *use : ig.lookup(mod)->uses()) {
416  auto inst = cast<InstanceOp>(use->getInstance().getOperation());
417  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
418  auto lhs = builder.create<WireOp>(inst.getResult(0).getType()).getResult();
419  auto rhs = builder.create<WireOp>(inst.getResult(1).getType()).getResult();
420  inst.getResult(0).replaceAllUsesWith(lhs);
421  inst.getResult(1).replaceAllUsesWith(rhs);
422  auto out = builder.create<LTLConcatIntrinsicOp>(lhs.getType(), lhs, rhs);
423  inst.getResult(2).replaceAllUsesWith(out);
424  inst.erase();
425  }
426  return true;
427 }
428 
429 static bool lowerCirctLTLNot(InstanceGraph &ig, FModuleLike mod) {
430  if (hasNPorts("circt.ltl.not", mod, 2) ||
431  namedPort("circt.ltl.not", mod, 0, "in") ||
432  namedPort("circt.ltl.not", mod, 1, "out") ||
433  sizedPort<UIntType>("circt.ltl.not", mod, 0, 1) ||
434  sizedPort<UIntType>("circt.ltl.not", mod, 1, 1) ||
435  hasNParam("circt.ltl.not", mod, 0))
436  return false;
437 
438  for (auto *use : ig.lookup(mod)->uses()) {
439  auto inst = cast<InstanceOp>(use->getInstance().getOperation());
440  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
441  auto input =
442  builder.create<WireOp>(inst.getResult(0).getType()).getResult();
443  inst.getResult(0).replaceAllUsesWith(input);
444  auto out = builder.create<LTLNotIntrinsicOp>(input.getType(), input);
445  inst.getResult(1).replaceAllUsesWith(out);
446  inst.erase();
447  }
448  return true;
449 }
450 
451 static bool lowerCirctLTLImplication(InstanceGraph &ig, FModuleLike mod) {
452  if (hasNPorts("circt.ltl.implication", mod, 3) ||
453  namedPort("circt.ltl.implication", mod, 0, "lhs") ||
454  namedPort("circt.ltl.implication", mod, 1, "rhs") ||
455  namedPort("circt.ltl.implication", mod, 2, "out") ||
456  sizedPort<UIntType>("circt.ltl.implication", mod, 0, 1) ||
457  sizedPort<UIntType>("circt.ltl.implication", mod, 1, 1) ||
458  sizedPort<UIntType>("circt.ltl.implication", mod, 2, 1) ||
459  hasNParam("circt.ltl.implication", mod, 0))
460  return false;
461 
462  for (auto *use : ig.lookup(mod)->uses()) {
463  auto inst = cast<InstanceOp>(use->getInstance().getOperation());
464  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
465  auto lhs = builder.create<WireOp>(inst.getResult(0).getType()).getResult();
466  auto rhs = builder.create<WireOp>(inst.getResult(1).getType()).getResult();
467  inst.getResult(0).replaceAllUsesWith(lhs);
468  inst.getResult(1).replaceAllUsesWith(rhs);
469  auto out =
470  builder.create<LTLImplicationIntrinsicOp>(lhs.getType(), lhs, rhs);
471  inst.getResult(2).replaceAllUsesWith(out);
472  inst.erase();
473  }
474  return true;
475 }
476 
477 static bool lowerCirctLTLEventually(InstanceGraph &ig, FModuleLike mod) {
478  if (hasNPorts("circt.ltl.eventually", mod, 2) ||
479  namedPort("circt.ltl.eventually", mod, 0, "in") ||
480  namedPort("circt.ltl.eventually", mod, 1, "out") ||
481  sizedPort<UIntType>("circt.ltl.eventually", mod, 0, 1) ||
482  sizedPort<UIntType>("circt.ltl.eventually", mod, 1, 1) ||
483  hasNParam("circt.ltl.eventually", mod, 0))
484  return false;
485 
486  for (auto *use : ig.lookup(mod)->uses()) {
487  auto inst = cast<InstanceOp>(use->getInstance().getOperation());
488  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
489  auto input =
490  builder.create<WireOp>(inst.getResult(0).getType()).getResult();
491  inst.getResult(0).replaceAllUsesWith(input);
492  auto out = builder.create<LTLEventuallyIntrinsicOp>(input.getType(), input);
493  inst.getResult(1).replaceAllUsesWith(out);
494  inst.erase();
495  }
496  return true;
497 }
498 
499 static bool lowerCirctLTLClock(InstanceGraph &ig, FModuleLike mod) {
500  if (hasNPorts("circt.ltl.clock", mod, 3) ||
501  namedPort("circt.ltl.clock", mod, 0, "in") ||
502  namedPort("circt.ltl.clock", mod, 1, "clock") ||
503  namedPort("circt.ltl.clock", mod, 2, "out") ||
504  sizedPort<UIntType>("circt.ltl.clock", mod, 0, 1) ||
505  typedPort<ClockType>("circt.ltl.clock", mod, 1) ||
506  sizedPort<UIntType>("circt.ltl.clock", mod, 2, 1) ||
507  hasNParam("circt.ltl.clock", mod, 0))
508  return false;
509 
510  for (auto *use : ig.lookup(mod)->uses()) {
511  auto inst = cast<InstanceOp>(use->getInstance().getOperation());
512  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
513  auto in = builder.create<WireOp>(inst.getResult(0).getType()).getResult();
514  auto clock =
515  builder.create<WireOp>(inst.getResult(1).getType()).getResult();
516  inst.getResult(0).replaceAllUsesWith(in);
517  inst.getResult(1).replaceAllUsesWith(clock);
518  auto out = builder.create<LTLClockIntrinsicOp>(in.getType(), in, clock);
519  inst.getResult(2).replaceAllUsesWith(out);
520  inst.erase();
521  }
522  return true;
523 }
524 
525 static bool lowerCirctLTLDisable(InstanceGraph &ig, FModuleLike mod) {
526  if (hasNPorts("circt.ltl.disable", mod, 3) ||
527  namedPort("circt.ltl.disable", mod, 0, "in") ||
528  namedPort("circt.ltl.disable", mod, 1, "condition") ||
529  namedPort("circt.ltl.disable", mod, 2, "out") ||
530  sizedPort<UIntType>("circt.ltl.disable", mod, 0, 1) ||
531  sizedPort<UIntType>("circt.ltl.disable", mod, 1, 1) ||
532  sizedPort<UIntType>("circt.ltl.disable", mod, 2, 1) ||
533  hasNParam("circt.ltl.disable", mod, 0))
534  return false;
535 
536  for (auto *use : ig.lookup(mod)->uses()) {
537  auto inst = cast<InstanceOp>(use->getInstance().getOperation());
538  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
539  auto in = builder.create<WireOp>(inst.getResult(0).getType()).getResult();
540  auto condition =
541  builder.create<WireOp>(inst.getResult(1).getType()).getResult();
542  inst.getResult(0).replaceAllUsesWith(in);
543  inst.getResult(1).replaceAllUsesWith(condition);
544  auto out =
545  builder.create<LTLDisableIntrinsicOp>(in.getType(), in, condition);
546  inst.getResult(2).replaceAllUsesWith(out);
547  inst.erase();
548  }
549  return true;
550 }
551 
552 template <class Op>
553 static bool lowerCirctVerif(InstanceGraph &ig, FModuleLike mod) {
554  if (hasNPorts("circt.verif.assert", mod, 1) ||
555  namedPort("circt.verif.assert", mod, 0, "property") ||
556  sizedPort<UIntType>("circt.verif.assert", mod, 0, 1) ||
557  hasNParam("circt.verif.assert", mod, 0, 1) ||
558  namedParam("circt.verif.assert", mod, "label", true))
559  return false;
560 
561  auto params = mod.getParameters();
562  StringAttr label;
563  if (!params.empty())
564  if (auto labelDecl = cast<ParamDeclAttr>(params[0]))
565  label = cast<StringAttr>(labelDecl.getValue());
566 
567  for (auto *use : ig.lookup(mod)->uses()) {
568  auto inst = cast<InstanceOp>(use->getInstance().getOperation());
569  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
570  auto property =
571  builder.create<WireOp>(inst.getResult(0).getType()).getResult();
572  inst.getResult(0).replaceAllUsesWith(property);
573  builder.create<Op>(property, label);
574  inst.erase();
575  }
576  return true;
577 }
578 
579 static bool lowerCirctHasBeenReset(InstanceGraph &ig, FModuleLike mod) {
580  if (hasNPorts("circt.has_been_reset", mod, 3) ||
581  namedPort("circt.has_been_reset", mod, 0, "clock") ||
582  namedPort("circt.has_been_reset", mod, 1, "reset") ||
583  namedPort("circt.has_been_reset", mod, 2, "out") ||
584  typedPort<ClockType>("circt.has_been_reset", mod, 0) ||
585  resetPort("circt.has_been_reset", mod, 1) ||
586  sizedPort<UIntType>("circt.has_been_reset", mod, 2, 1) ||
587  hasNParam("circt.has_been_reset", mod, 0))
588  return false;
589 
590  for (auto *use : ig.lookup(mod)->uses()) {
591  auto inst = cast<InstanceOp>(use->getInstance().getOperation());
592  ImplicitLocOpBuilder builder(inst.getLoc(), inst);
593  auto clock =
594  builder.create<WireOp>(inst.getResult(0).getType()).getResult();
595  auto reset =
596  builder.create<WireOp>(inst.getResult(1).getType()).getResult();
597  inst.getResult(0).replaceAllUsesWith(clock);
598  inst.getResult(1).replaceAllUsesWith(reset);
599  auto out = builder.create<HasBeenResetIntrinsicOp>(clock, reset);
600  inst.getResult(2).replaceAllUsesWith(out);
601  inst.erase();
602  }
603  return true;
604 }
605 
606 std::pair<const char *, std::function<bool(InstanceGraph &, FModuleLike)>>
608  {"circt.sizeof", lowerCirctSizeof},
609  {"circt_sizeof", lowerCirctSizeof},
610  {"circt.isX", lowerCirctIsX},
611  {"circt_isX", lowerCirctIsX},
612  {"circt.plusargs.test", lowerCirctPlusArgTest},
613  {"circt_plusargs_test", lowerCirctPlusArgTest},
614  {"circt.plusargs.value", lowerCirctPlusArgValue},
615  {"circt_plusargs_value", lowerCirctPlusArgValue},
616  {"circt.clock_gate", lowerCirctClockGate},
617  {"circt_clock_gate", lowerCirctClockGate},
618  {"circt.ltl.and", lowerCirctLTLAnd},
619  {"circt_ltl_and", lowerCirctLTLAnd},
620  {"circt.ltl.or", lowerCirctLTLOr},
621  {"circt_ltl_or", lowerCirctLTLOr},
622  {"circt.ltl.delay", lowerCirctLTLDelay},
623  {"circt_ltl_delay", lowerCirctLTLDelay},
624  {"circt.ltl.concat", lowerCirctLTLConcat},
625  {"circt_ltl_concat", lowerCirctLTLConcat},
626  {"circt.ltl.not", lowerCirctLTLNot},
627  {"circt_ltl_not", lowerCirctLTLNot},
628  {"circt.ltl.implication", lowerCirctLTLImplication},
629  {"circt_ltl_implication", lowerCirctLTLImplication},
630  {"circt.ltl.eventually", lowerCirctLTLEventually},
631  {"circt_ltl_eventually", lowerCirctLTLEventually},
632  {"circt.ltl.clock", lowerCirctLTLClock},
633  {"circt_ltl_clock", lowerCirctLTLClock},
634  {"circt.ltl.disable", lowerCirctLTLDisable},
635  {"circt_ltl_disable", lowerCirctLTLDisable},
636  {"circt.verif.assert", lowerCirctVerif<VerifAssertIntrinsicOp>},
637  {"circt_verif_assert", lowerCirctVerif<VerifAssertIntrinsicOp>},
638  {"circt.verif.assume", lowerCirctVerif<VerifAssumeIntrinsicOp>},
639  {"circt_verif_assume", lowerCirctVerif<VerifAssumeIntrinsicOp>},
640  {"circt.verif.cover", lowerCirctVerif<VerifCoverIntrinsicOp>},
641  {"circt_verif_cover", lowerCirctVerif<VerifCoverIntrinsicOp>},
642  {"circt.mux2cell", lowerCirctMuxCell<true>},
643  {"circt_mux2cell", lowerCirctMuxCell<true>},
644  {"circt.mux4cell", lowerCirctMuxCell<false>},
645  {"circt_mux4cell", lowerCirctMuxCell<false>},
646  {"circt.has_been_reset", lowerCirctHasBeenReset},
647  {"circt_has_been_reset", lowerCirctHasBeenReset}};
648 
649 // This is the main entrypoint for the lowering pass.
650 void LowerIntrinsicsPass::runOnOperation() {
651  size_t numFailures = 0;
652  size_t numConverted = 0;
653  InstanceGraph &ig = getAnalysis<InstanceGraph>();
654  for (auto &op : llvm::make_early_inc_range(getOperation().getOps())) {
655  if (!isa<FExtModuleOp, FIntModuleOp>(op))
656  continue;
657  StringAttr intname;
658  if (isa<FExtModuleOp>(op)) {
659  auto anno = AnnotationSet(&op).getAnnotation("circt.Intrinsic");
660  if (!anno)
661  continue;
662  intname = anno.getMember<StringAttr>("intrinsic");
663  if (!intname) {
664  op.emitError("intrinsic annotation with no intrinsic name");
665  ++numFailures;
666  continue;
667  }
668  } else {
669  intname = cast<FIntModuleOp>(op).getIntrinsicAttr();
670  if (!intname) {
671  op.emitError("intrinsic module with no intrinsic name");
672  ++numFailures;
673  continue;
674  }
675  }
676 
677  bool found = false;
678  for (const auto &intrinsic : intrinsics) {
679  if (intname.getValue().equals(intrinsic.first)) {
680  found = true;
681  if (intrinsic.second(ig, cast<FModuleLike>(op))) {
682  ++numConverted;
683  op.erase();
684  } else {
685  ++numFailures;
686  }
687  break;
688  }
689  }
690  if (!found) {
691  op.emitError("unknown intrinsic: '") << intname.getValue() << "'";
692  ++numFailures;
693  }
694  }
695  if (numFailures)
696  signalPassFailure();
697  if (!numConverted)
698  markAllAnalysesPreserved();
699 }
700 
701 /// This is the pass constructor.
702 std::unique_ptr<mlir::Pass> circt::firrtl::createLowerIntrinsicsPass() {
703  return std::make_unique<LowerIntrinsicsPass>();
704 }
lowerAnnotationsNoRefTypePorts FirtoolPreserveValuesMode value
Definition: Firtool.cpp:95
static bool lowerCirctHasBeenReset(InstanceGraph &ig, FModuleLike mod)
static bool lowerCirctPlusArgValue(InstanceGraph &ig, FModuleLike mod)
static bool lowerCirctMuxCell(InstanceGraph &ig, FModuleLike mod)
static ParseResult namedPort(StringRef name, FModuleLike mod, unsigned n, StringRef portName)
static bool lowerCirctPlusArgTest(InstanceGraph &ig, FModuleLike mod)
static ParseResult hasNParam(StringRef name, FModuleLike mod, unsigned n, unsigned c=0)
static ParseResult namedParam(StringRef name, FModuleLike mod, StringRef paramName, bool optional=false)
static bool lowerCirctLTLEventually(InstanceGraph &ig, FModuleLike mod)
static bool lowerCirctLTLClock(InstanceGraph &ig, FModuleLike mod)
static bool lowerCirctLTLImplication(InstanceGraph &ig, FModuleLike mod)
std::pair< const char *, std::function< bool(InstanceGraph &, FModuleLike)> > intrinsics[]
static bool lowerCirctIsX(InstanceGraph &ig, FModuleLike mod)
static bool lowerCirctLTLConcat(InstanceGraph &ig, FModuleLike mod)
static bool lowerCirctVerif(InstanceGraph &ig, FModuleLike mod)
static bool lowerCirctLTLAnd(InstanceGraph &ig, FModuleLike mod)
static bool lowerCirctSizeof(InstanceGraph &ig, FModuleLike mod)
static ParseResult sizedPort(StringRef name, FModuleLike mod, unsigned n, int32_t size)
static bool lowerCirctClockGate(InstanceGraph &ig, FModuleLike mod)
static ParseResult resetPort(StringRef name, FModuleLike mod, unsigned n)
static bool lowerCirctLTLDelay(InstanceGraph &ig, FModuleLike mod)
static ParseResult namedIntParam(StringRef name, FModuleLike mod, StringRef paramName, bool optional=false)
static bool lowerCirctLTLOr(InstanceGraph &ig, FModuleLike mod)
static ParseResult hasNPorts(StringRef name, FModuleLike mod, unsigned n)
static bool lowerCirctLTLDisable(InstanceGraph &ig, FModuleLike mod)
static bool lowerCirctLTLNot(InstanceGraph &ig, FModuleLike mod)
static ParseResult typedPort(StringRef name, FModuleLike mod, unsigned n)
Builder builder
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
This graph tracks modules and where they are instantiated.
llvm::iterator_range< UseIterator > uses()
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:53
uint64_t getWidth(Type t)
Definition: ESIPasses.cpp:34
std::unique_ptr< mlir::Pass > createLowerIntrinsicsPass()
This is the pass constructor.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
Definition: DebugAnalysis.h:21