Skip to content

Commit

Permalink
[FIRRTL] Move SIntType and UIntType to ODS (#4529)
Browse files Browse the repository at this point in the history
- Make WidthQualifiedTrait a type trait, renaming it to
  WidthQualifiedTypeTrait.
- Rather than have users implement getWidth(), have users implement
  getWidthOrSentinel().
  - enshrine the idea that the "widthOrSentinel/int32_t" is the most primitive
    format for widths of types.
  - this means that if a type has an attribute called widthOrSentinel, then it
    can use this trait for free
  - rename to WidthQualifiedTypeTrait
- Remove WidthQualifiedType and WidthTypeStorage
- In AnalogType, rename the widthBase attribute to widthOrSentinel
- Make IntType use WidthQualifiedTypeTrait
- Verify the widthOrSentinel value in a type verifier, rather than in the builder
- Add a verifier for AnalogType
- Let FIRRTLImplType take a baseCppClass parameter
  - Used to let S/UIntType to inherit from IntType.
  • Loading branch information
rwy7 authored Jan 20, 2023
1 parent 734f626 commit b6d8508
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 131 deletions.
100 changes: 25 additions & 75 deletions include/circt/Dialect/FIRRTL/FIRRTLTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,108 +179,58 @@ mlir::Type getPassiveType(mlir::Type anyBaseFIRRTLType);
// Width Qualified Ground Types
//===----------------------------------------------------------------------===//

/// Trait for types which have a width.
/// Users must implement:
/// ```c++
/// /// Return the width if known, or -1 if unknown.
/// int32_t getWidthOrSentinel();
/// ```
template <typename ConcreteType>
class WidthQualifiedTrait
: public mlir::OpTrait::TraitBase<ConcreteType, WidthQualifiedTrait> {
class WidthQualifiedTypeTrait
: public mlir::TypeTrait::TraitBase<ConcreteType, WidthQualifiedTypeTrait> {
public:
/// Return an optional containing the width, if the width is known (or empty
/// if width is unknown).
std::optional<int32_t> getWidth() {
auto v = static_cast<ConcreteType *>(this)->getBaseWidth();
if (v >= 0)
return v;
return {};
}
int32_t getWidthOrSentinel() {
return static_cast<ConcreteType *>(this)->getBaseWidth();
auto width = static_cast<ConcreteType *>(this)->getWidthOrSentinel();
if (width < 0)
return std::nullopt;
return width;
}

/// Return true if this integer type has a known width.
bool hasWidth() {
return static_cast<ConcreteType *>(this)->getBaseWidth() >= 0;
}
ConcreteType changeWidth(int32_t width) {
return ConcreteType::get(static_cast<ConcreteType *>(this)->getContext(),
width);
return 0 <= static_cast<ConcreteType *>(this)->getWidthOrSentinel();
}
};

template <typename ConcreteType, typename ParentType>
class WidthQualifiedType
: public FIRRTLType::TypeBase<ConcreteType, ParentType,
detail::WidthTypeStorage,
circt::hw::FieldIDTypeInterface::Trait> {
public:
using FIRRTLType::TypeBase<
ConcreteType, ParentType, detail::WidthTypeStorage,
circt::hw::FieldIDTypeInterface::Trait>::Base::Base;

/// Return the bitwidth of this type or None if unknown.
std::optional<int32_t> getWidth() {
return static_cast<ConcreteType *>(this)->getWidth();
}

/// Return the width of this type, or -1 if it has none specified.
int32_t getWidthOrSentinel() { return getWidth().value_or(-1); }

/// Return true if this type has a known width.
bool hasWidth() { return getWidth().has_value(); }

/// Return a new type with the width changed to a different value.
ConcreteType changeWidth(int32_t width) {
return ConcreteType::get(static_cast<ConcreteType *>(this)->getContext(),
width);
}
};
//===----------------------------------------------------------------------===//
// IntType
//===----------------------------------------------------------------------===//

class SIntType;
class UIntType;

/// This is the common base class between SIntType and UIntType.
class IntType : public FIRRTLBaseType {
class IntType : public FIRRTLBaseType, public WidthQualifiedTypeTrait<IntType> {
public:
using FIRRTLBaseType::FIRRTLBaseType;

/// Return a SIntType or UInt type with the specified signedness and width.
static IntType get(MLIRContext *context, bool isSigned, int32_t width = -1);
/// Return an SIntType or UIntType with the specified signedness and width.
static IntType get(MLIRContext *context, bool isSigned,
int32_t widthOrSentinel = -1);

bool isSigned() { return isa<SIntType>(); }
bool isUnsigned() { return isa<UIntType>(); }

/// Return true if this integer type has a known width.
bool hasWidth() { return getWidth().has_value(); }

/// Return the bitwidth of this type or None if unknown.
std::optional<int32_t> getWidth();

/// Return the width of this type, or -1 if it has none specified.
int32_t getWidthOrSentinel() { return getWidth().value_or(-1); }
int32_t getWidthOrSentinel();

static bool classof(Type type) {
return type.isa<SIntType>() || type.isa<UIntType>();
}
};

/// A signed integer type, whose width may not be known.
class SIntType : public WidthQualifiedType<SIntType, IntType> {
public:
using WidthQualifiedType::WidthQualifiedType;

/// Get an with a known width, or -1 for unknown.
static SIntType get(MLIRContext *context, int32_t width = -1);

/// Return the bitwidth of this type or None if unknown.
std::optional<int32_t> getWidth();
};

/// An unsigned integer type, whose width may not be known.
class UIntType : public WidthQualifiedType<UIntType, IntType> {
public:
using WidthQualifiedType::WidthQualifiedType;

/// Get an with a known width, or -1 for unknown.
static UIntType get(MLIRContext *context, int32_t width = -1);

/// Return the bitwidth of this type or None if unknown.
std::optional<int32_t> getWidth();
};

//===----------------------------------------------------------------------===//
// Reference Type
//===----------------------------------------------------------------------===//
Expand Down
55 changes: 47 additions & 8 deletions include/circt/Dialect/FIRRTL/FIRRTLTypesImpl.td
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,55 @@ include "FIRRTLDialect.td"
include "circt/Dialect/HW/HWTypeInterfaces.td"

// Base class for other typedefs. Provides dialact-specific defaults.
class FIRRTLImplType<string name, list<Trait> traits = []>
: TypeDef<FIRRTLDialect, name, traits, "::circt::firrtl::FIRRTLBaseType"> {}
class FIRRTLImplType<string name,
list<Trait> traits = [],
string baseCppClass = "::circt::firrtl::FIRRTLBaseType">
: TypeDef<FIRRTLDialect, name, traits, baseCppClass> {}

def WidthQualifiedTrait : NativeTypeTrait<"WidthQualifiedTrait"> {
//===----------------------------------------------------------------------===//
// Type Traits
//===----------------------------------------------------------------------===//

def WidthQualifiedTypeTrait : NativeTypeTrait<"WidthQualifiedTypeTrait"> {
let cppNamespace = "::circt::firrtl";
}

//===----------------------------------------------------------------------===//
// Type declarations
// Type Definitions
//===----------------------------------------------------------------------===//

def SIntImpl : FIRRTLImplType<"SInt",
[WidthQualifiedTypeTrait, FieldIDTypeInterface],
"::circt::firrtl::IntType"> {
let summary = "A signed integer type, whose width may not be known.";
let parameters = (ins "int32_t":$widthOrSentinel);
let builders = [
TypeBuilder<(ins "std::optional<int32_t>":$width)>,
TypeBuilder<(ins)>,
];
let genVerifyDecl = true;
let extraClassDeclaration = [{
using WidthQualifiedTypeTrait<SIntType>::getWidth;
using WidthQualifiedTypeTrait<SIntType>::hasWidth;
}];
}

def UIntImpl : FIRRTLImplType<"UInt",
[WidthQualifiedTypeTrait, FieldIDTypeInterface],
"::circt::firrtl::IntType"> {
let summary = "An unsigned integer type, whose width may not be known.";
let parameters = (ins "int32_t":$widthOrSentinel);
let builders = [
TypeBuilder<(ins "std::optional<int32_t>":$width)>,
TypeBuilder<(ins)>,
];
let genVerifyDecl = true;
let extraClassDeclaration = [{
using WidthQualifiedTypeTrait<UIntType>::getWidth;
using WidthQualifiedTypeTrait<UIntType>::hasWidth;
}];
}

def ClockTypeImpl : FIRRTLImplType<"Clock", [FieldIDTypeInterface]> {
let summary = "Clock signal";
}
Expand All @@ -40,14 +79,14 @@ def AsyncResetTypeImpl : FIRRTLImplType<"AsyncReset", [FieldIDTypeInterface]> {
}

def AnalogTypeImpl : FIRRTLImplType<"Analog",
[WidthQualifiedTrait, FieldIDTypeInterface]> {
[WidthQualifiedTypeTrait, FieldIDTypeInterface]> {
let summary = "Analog signal";
let parameters = (ins "int32_t":$baseWidth);
let parameters = (ins "int32_t":$widthOrSentinel);
let builders = [
TypeBuilder<(ins "int32_t":$baseWidth)>,
TypeBuilder<(ins "std::optional<int32_t>":$width)>,
TypeBuilder<(ins)>,
];

let genVerifyDecl = true;
}

def FVectorTypeImpl : FIRRTLImplType<"FVector", [FieldIDTypeInterface]> {
Expand Down
100 changes: 52 additions & 48 deletions lib/Dialect/FIRRTL/FIRRTLTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -701,68 +701,58 @@ Type firrtl::getPassiveType(Type anyBaseFIRRTLType) {
// IntType
//===----------------------------------------------------------------------===//

/// Return the bitwidth of this type or None if unknown.
std::optional<int32_t> IntType::getWidth() {
return isSigned() ? this->cast<SIntType>().getWidth()
: this->cast<UIntType>().getWidth();
}

/// Return a SIntType or UInt type with the specified signedness and width.
IntType IntType::get(MLIRContext *context, bool isSigned, int32_t width) {
IntType IntType::get(MLIRContext *context, bool isSigned,
int32_t widthOrSentinel) {
if (isSigned)
return SIntType::get(context, width);
return UIntType::get(context, width);
return SIntType::get(context, widthOrSentinel);
return UIntType::get(context, widthOrSentinel);
}

int32_t IntType::getWidthOrSentinel() {
if (isa<SIntType>())
return this->cast<SIntType>().getWidthOrSentinel();
if (isa<UIntType>())
return this->cast<UIntType>().getWidthOrSentinel();
return -1;
}

//===----------------------------------------------------------------------===//
// Width Qualified Ground Types
// SIntType
//===----------------------------------------------------------------------===//

namespace circt {
namespace firrtl {
namespace detail {
struct WidthTypeStorage : mlir::TypeStorage {
WidthTypeStorage(int32_t width) : width(width) {}
using KeyTy = int32_t;

bool operator==(const KeyTy &key) const { return key == width; }

static WidthTypeStorage *construct(TypeStorageAllocator &allocator,
const KeyTy &key) {
return new (allocator.allocate<WidthTypeStorage>()) WidthTypeStorage(key);
}

int32_t width;
};
} // namespace detail
} // namespace firrtl
} // namespace circt
SIntType SIntType::get(MLIRContext *context) { return get(context, -1); }

static std::optional<int32_t>
getWidthQualifiedTypeWidth(firrtl::detail::WidthTypeStorage *impl) {
int width = impl->width;
if (width < 0)
return std::nullopt;
return width;
SIntType SIntType::get(MLIRContext *context, std::optional<int32_t> width) {
if (!width)
return get(context);
return get(context, *width);
}

/// Get an with a known width, or -1 for unknown.
SIntType SIntType::get(MLIRContext *context, int32_t width) {
assert(width >= -1 && "unknown width");
return Base::get(context, width);
LogicalResult SIntType::verify(function_ref<InFlightDiagnostic()> emitError,
int32_t widthOrSentinel) {
if (widthOrSentinel < -1)
return emitError() << "invalid width";
return success();
}

std::optional<int32_t> SIntType::getWidth() {
return getWidthQualifiedTypeWidth(this->getImpl());
}
//===----------------------------------------------------------------------===//
// UIntType
//===----------------------------------------------------------------------===//

UIntType UIntType::get(MLIRContext *context) { return get(context, -1); }

UIntType UIntType::get(MLIRContext *context, int32_t width) {
assert(width >= -1 && "unknown width");
return Base::get(context, width);
UIntType UIntType::get(MLIRContext *context, std::optional<int32_t> width) {
if (!width)
return get(context);
return get(context, *width);
}

std::optional<int32_t> UIntType::getWidth() {
return getWidthQualifiedTypeWidth(this->getImpl());
LogicalResult UIntType::verify(function_ref<InFlightDiagnostic()> emitError,
int32_t widthOrSentinel) {
if (widthOrSentinel < -1)
return emitError() << "invalid width";
return success();
}

//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -1081,13 +1071,27 @@ auto RefType::verify(function_ref<InFlightDiagnostic()> emitErrorFn,
}

//===----------------------------------------------------------------------===//
// ODS Custom Builders
// AnalogType
//===----------------------------------------------------------------------===//

AnalogType AnalogType::get(mlir::MLIRContext *context) {
return AnalogType::get(context, -1);
}

AnalogType AnalogType::get(mlir::MLIRContext *context,
std::optional<int32_t> width) {
if (!width)
return AnalogType::get(context);
return AnalogType::get(context, *width);
}

LogicalResult AnalogType::verify(function_ref<InFlightDiagnostic()> emitError,
int32_t widthOrSentinel) {
if (widthOrSentinel < -1)
return emitError() << "invalid width";
return success();
}

//===----------------------------------------------------------------------===//
// FIRRTLDialect
//===----------------------------------------------------------------------===//
Expand Down

0 comments on commit b6d8508

Please sign in to comment.