CIRCT  19.0.0git
APIUtilities.cpp
Go to the documentation of this file.
1 //===- APIUtilities.cpp - ESI general-purpose API utilities ------- 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 // Utilities and classes applicable to all ESI API generators.
10 //
11 //===----------------------------------------------------------------------===//
12 
17 #include "circt/Dialect/HW/HWOps.h"
19 #include "circt/Dialect/SV/SVOps.h"
20 #include "mlir/Support/IndentedOstream.h"
21 
22 #include "mlir/Dialect/Func/IR/FuncOps.h"
23 #include "mlir/IR/Builders.h"
24 #include "mlir/IR/BuiltinAttributes.h"
25 #include "mlir/IR/BuiltinTypes.h"
26 #include "llvm/ADT/IntervalMap.h"
27 #include "llvm/ADT/TypeSwitch.h"
28 #include "llvm/Support/Format.h"
29 
30 namespace circt {
31 namespace esi {
32 
33 /// Returns true if the type is currently supported.
34 // NOLINTNEXTLINE(misc-no-recursion)
35 static bool isSupported(Type type, bool outer = false) {
36  return llvm::TypeSwitch<::mlir::Type, bool>(type)
37  .Case([](IntegerType t) { return t.getWidth() <= 64; })
38  .Case([](hw::ArrayType t) { return isSupported(t.getElementType()); })
39  .Case([outer](hw::StructType t) {
40  // We don't yet support structs containing structs.
41  if (!outer)
42  return false;
43  // A struct is supported if all of its elements are.
44  for (auto field : t.getElements()) {
45  if (!isSupported(field.type))
46  return false;
47  }
48  return true;
49  })
50  .Default([](Type) { return false; });
51 }
52 
54  return circt::esi::isSupported(type, true);
55 }
56 
57 uint64_t ESIAPIType::size() const {
58  if (auto chan = dyn_cast<ChannelType>(type))
59  return hw::getBitWidth(chan.getInner());
60  return hw::getBitWidth(type);
61 }
62 
63 ESIAPIType::ESIAPIType(Type typeArg) : type(innerType(typeArg)) {
64  TypeSwitch<Type>(type)
65  .Case([this](IntegerType t) {
66  fieldTypes.push_back(
67  FieldInfo{StringAttr::get(t.getContext(), "i"), t});
68  })
69  .Case([this](hw::ArrayType t) {
70  fieldTypes.push_back(
71  FieldInfo{StringAttr::get(t.getContext(), "l"), t});
72  })
73  .Case([this](hw::StructType t) {
74  fieldTypes.append(t.getElements().begin(), t.getElements().end());
75  })
76  .Default([](Type) {});
77 }
78 
79 bool ESIAPIType::operator==(const ESIAPIType &that) const {
80  return type == that.type;
81 }
82 
83 /// Write a valid Capnp name for 'type'.
84 // NOLINTNEXTLINE(misc-no-recursion)
85 static void emitName(Type type, uint64_t id, llvm::raw_ostream &os) {
86  llvm::TypeSwitch<Type>(type)
87  .Case([&os](IntegerType intTy) {
88  std::string intName;
89  llvm::raw_string_ostream(intName) << intTy;
90  // Capnp struct names must start with an uppercase character.
91  intName[0] = toupper(intName[0]);
92  os << intName;
93  })
94  .Case([&os](hw::ArrayType arrTy) {
95  os << "ArrayOf" << arrTy.getNumElements() << 'x';
96  emitName(arrTy.getElementType(), 0, os);
97  })
98  .Case([&os](NoneType) { os << "None"; })
99  .Case([&os, id](hw::StructType t) { os << "Struct" << id; })
100  .Default([](Type) {
101  assert(false && "Type not supported. Please check support first with "
102  "isSupported()");
103  });
104 }
105 
106 /// For now, the name is just the type serialized. This works only because we
107 /// only support ints.
108 StringRef ESIAPIType::name() const {
109  if (cachedName.empty()) {
110  llvm::raw_string_ostream os(cachedName);
111  emitName(type, typeID(), os);
112  cachedName = os.str();
113  }
114  return cachedName;
115 }
116 
117 // We compute a deterministic hash based on the type. Since llvm::hash_value
118 // changes from execution to execution, we don't use it.
119 uint64_t ESIAPIType::typeID() const {
120  if (cachedID)
121  return *cachedID;
122 
123  // Get the MLIR asm type, padded to a multiple of 64 bytes.
124  std::string typeName;
125  llvm::raw_string_ostream osName(typeName);
126  osName << type;
127  size_t overhang = osName.tell() % 64;
128  if (overhang != 0)
129  osName.indent(64 - overhang);
130  osName.flush();
131  const char *typeNameC = typeName.c_str();
132 
133  uint64_t hash = esiApiVersion;
134  for (size_t i = 0, e = typeName.length() / 64; i < e; ++i)
135  hash =
136  llvm::hashing::detail::hash_33to64_bytes(&typeNameC[i * 64], 64, hash);
137 
138  // Capnp IDs always have a '1' high bit.
139  cachedID = hash | 0x8000000000000000;
140  return *cachedID;
141 }
142 
143 } // namespace esi
144 } // namespace circt
assert(baseType &&"element must be base type")
StringRef name() const
For now, the name is just the type serialized.
hw::StructType::FieldInfo FieldInfo
Definition: APIUtilities.h:33
bool operator==(const ESIAPIType &) const
std::string cachedName
Definition: APIUtilities.h:61
llvm::SmallVector< FieldInfo > fieldTypes
Cosim requires that everything be contained in a struct.
Definition: APIUtilities.h:58
uint64_t size() const
std::optional< uint64_t > cachedID
Definition: APIUtilities.h:62
uint64_t typeID() const
virtual bool isSupported() const
Returns true if the type is currently supported.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
constexpr uint64_t esiApiVersion
Every time we implement a breaking change in the schema generation, increment this number.
Definition: APIUtilities.h:28
mlir::Type innerType(mlir::Type type)
Definition: ESITypes.cpp:184
static void emitName(Type type, uint64_t id, llvm::raw_ostream &os)
Write a valid Capnp name for 'type'.
static bool isSupported(Type type, bool outer=false)
Returns true if the type is currently supported.
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
Definition: HWTypes.cpp:102
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
Definition: esi.py:1