[RTG] Add immediate type and attribute (#8314)

This adds an immediate type and attribute that support any bitwidth and will replace the immediate types and attributes in the RTGTest dialect. Also adds a constant operation that can create an SSA value for any typed attribute. I also moved the labels type to form a kind of ISA subdialect with the immediates and made that explicit in the textual IR. We can do the same for relevant ops in the future.
This commit is contained in:
Martin Erhart 2025-04-01 09:53:51 +01:00 committed by GitHub
parent 475b936c55
commit 1db8b4dec7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 488 additions and 92 deletions

View File

@ -20,10 +20,10 @@ class Tgt0:
return Set.create(Integer(0), Integer(1))
# MLIR-LABEL: rtg.target @Tgt1 : !rtg.dict<entry0: index, entry1: !rtg.label>
# MLIR-LABEL: rtg.target @Tgt1 : !rtg.dict<entry0: index, entry1: !rtg.isa.label>
# MLIR-NEXT: [[C0:%.+]] = index.constant 0
# MLIR-NEXT: [[LBL:%.+]] = rtg.label_decl "l0"
# MLIR-NEXT: rtg.yield [[C0]], [[LBL]] : index, !rtg.label
# MLIR-NEXT: rtg.yield [[C0]], [[LBL]] : index, !rtg.isa.label
# MLIR-NEXT: }
@ -40,7 +40,7 @@ class Tgt1:
# MLIR-LABEL: rtg.sequence @seq0
# MLIR-SAME: ([[SET:%.+]]: !rtg.set<!rtg.label>)
# MLIR-SAME: ([[SET:%.+]]: !rtg.set<!rtg.isa.label>)
# MLIR-NEXT: [[LABEL:%.+]] = rtg.set_select_random [[SET]]
# MLIR-NEXT: rtg.label local [[LABEL]]
# MLIR-NEXT: }
@ -114,15 +114,15 @@ def test_args(set: Set):
# MLIR-NEXT: rtg.label external [[L1]]
# MLIR-NEXT: rtg.label local [[L2]]
# MLIR-NEXT: [[SET0:%.+]] = rtg.set_create [[L0]], [[L1]] : !rtg.label
# MLIR-NEXT: [[SET1:%.+]] = rtg.set_create [[L2]] : !rtg.label
# MLIR-NEXT: [[EMPTY_SET:%.+]] = rtg.set_create : !rtg.label
# MLIR-NEXT: [[SET2_1:%.+]] = rtg.set_union [[SET0]], [[SET1]] : !rtg.set<!rtg.label>
# MLIR-NEXT: [[SET2:%.+]] = rtg.set_union [[SET2_1]], [[EMPTY_SET]] : !rtg.set<!rtg.label>
# MLIR-NEXT: [[RL0:%.+]] = rtg.set_select_random [[SET2]] : !rtg.set<!rtg.label>
# MLIR-NEXT: [[SET0:%.+]] = rtg.set_create [[L0]], [[L1]] : !rtg.isa.label
# MLIR-NEXT: [[SET1:%.+]] = rtg.set_create [[L2]] : !rtg.isa.label
# MLIR-NEXT: [[EMPTY_SET:%.+]] = rtg.set_create : !rtg.isa.label
# MLIR-NEXT: [[SET2_1:%.+]] = rtg.set_union [[SET0]], [[SET1]] : !rtg.set<!rtg.isa.label>
# MLIR-NEXT: [[SET2:%.+]] = rtg.set_union [[SET2_1]], [[EMPTY_SET]] : !rtg.set<!rtg.isa.label>
# MLIR-NEXT: [[RL0:%.+]] = rtg.set_select_random [[SET2]] : !rtg.set<!rtg.isa.label>
# MLIR-NEXT: rtg.label local [[RL0]]
# MLIR-NEXT: [[SET2_MINUS_SET0:%.+]] = rtg.set_difference [[SET2]], [[SET0]] : !rtg.set<!rtg.label>
# MLIR-NEXT: [[RL1:%.+]] = rtg.set_select_random [[SET2_MINUS_SET0]] : !rtg.set<!rtg.label>
# MLIR-NEXT: [[SET2_MINUS_SET0:%.+]] = rtg.set_difference [[SET2]], [[SET0]] : !rtg.set<!rtg.isa.label>
# MLIR-NEXT: [[RL1:%.+]] = rtg.set_select_random [[SET2_MINUS_SET0]] : !rtg.set<!rtg.isa.label>
# MLIR-NEXT: rtg.label local [[RL1]]
# MLIR-NEXT: rtg.label_decl "L_{{[{][{]0[}][}]}}", %idx5
@ -130,21 +130,21 @@ def test_args(set: Set):
# MLIR-NEXT: rtg.label_decl "L_{{[{][{]0[}][}]}}", %idx3
# MLIR-NEXT: rtg.label local
# MLIR-NEXT: [[BAG0:%.+]] = rtg.bag_create (%idx2 x [[L0:%.+]], %idx1 x [[L1:%.+]]) : !rtg.label
# MLIR-NEXT: [[BAG1:%.+]] = rtg.bag_create (%idx1 x [[L2:%.+]]) : !rtg.label
# MLIR-NEXT: [[EMPTY_BAG:%.+]] = rtg.bag_create : !rtg.label
# MLIR-NEXT: [[BAG2_1:%.+]] = rtg.bag_union [[BAG0]], [[BAG1]] : !rtg.bag<!rtg.label>
# MLIR-NEXT: [[BAG2:%.+]] = rtg.bag_union [[BAG2_1]], [[EMPTY_BAG]] : !rtg.bag<!rtg.label>
# MLIR-NEXT: [[RL2:%.+]] = rtg.bag_select_random [[BAG2]] : !rtg.bag<!rtg.label>
# MLIR-NEXT: [[SUB:%.+]] = rtg.bag_create (%idx1 x [[RL2]]) : !rtg.label
# MLIR-NEXT: [[BAG3:%.+]] = rtg.bag_difference [[BAG2]], [[SUB]] inf : !rtg.bag<!rtg.label>
# MLIR-NEXT: [[BAG0:%.+]] = rtg.bag_create (%idx2 x [[L0:%.+]], %idx1 x [[L1:%.+]]) : !rtg.isa.label
# MLIR-NEXT: [[BAG1:%.+]] = rtg.bag_create (%idx1 x [[L2:%.+]]) : !rtg.isa.label
# MLIR-NEXT: [[EMPTY_BAG:%.+]] = rtg.bag_create : !rtg.isa.label
# MLIR-NEXT: [[BAG2_1:%.+]] = rtg.bag_union [[BAG0]], [[BAG1]] : !rtg.bag<!rtg.isa.label>
# MLIR-NEXT: [[BAG2:%.+]] = rtg.bag_union [[BAG2_1]], [[EMPTY_BAG]] : !rtg.bag<!rtg.isa.label>
# MLIR-NEXT: [[RL2:%.+]] = rtg.bag_select_random [[BAG2]] : !rtg.bag<!rtg.isa.label>
# MLIR-NEXT: [[SUB:%.+]] = rtg.bag_create (%idx1 x [[RL2]]) : !rtg.isa.label
# MLIR-NEXT: [[BAG3:%.+]] = rtg.bag_difference [[BAG2]], [[SUB]] inf : !rtg.bag<!rtg.isa.label>
# MLIR-NEXT: rtg.label local [[RL2]]
# MLIR-NEXT: [[BAG4:%.+]] = rtg.bag_difference [[BAG3]], [[BAG1]] : !rtg.bag<!rtg.label>
# MLIR-NEXT: [[RL3:%.+]] = rtg.bag_select_random [[BAG4]] : !rtg.bag<!rtg.label>
# MLIR-NEXT: [[BAG4:%.+]] = rtg.bag_difference [[BAG3]], [[BAG1]] : !rtg.bag<!rtg.isa.label>
# MLIR-NEXT: [[RL3:%.+]] = rtg.bag_select_random [[BAG4]] : !rtg.bag<!rtg.isa.label>
# MLIR-NEXT: rtg.label local [[RL3]]
# MLIR-NEXT: [[SEQ:%.+]] = rtg.get_sequence @seq0 : !rtg.sequence<!rtg.set<!rtg.label>>
# MLIR-NEXT: [[SUBST:%.+]] = rtg.substitute_sequence [[SEQ]]([[SET0]]) : !rtg.sequence<!rtg.set<!rtg.label>>
# MLIR-NEXT: [[SEQ:%.+]] = rtg.get_sequence @seq0 : !rtg.sequence<!rtg.set<!rtg.isa.label>>
# MLIR-NEXT: [[SUBST:%.+]] = rtg.substitute_sequence [[SEQ]]([[SET0]]) : !rtg.sequence<!rtg.set<!rtg.isa.label>>
# MLIR-NEXT: [[RAND1:%.+]] = rtg.randomize_sequence [[SUBST]]
# MLIR-NEXT: rtg.embed_sequence [[RAND1]]
# MLIR-NEXT: [[RAND2:%.+]] = rtg.randomize_sequence [[SUBST]]

View File

@ -79,6 +79,16 @@ MLIR_CAPI_EXPORTED MlirType rtgDictTypeGet(MlirContext ctxt,
MlirAttribute const *entryNames,
MlirType const *entryTypes);
/// If the type is an RTG immediate.
MLIR_CAPI_EXPORTED bool rtgTypeIsAImmediate(MlirType type);
/// Creates an RTG immediate type in the context.
MLIR_CAPI_EXPORTED MlirType rtgImmediateTypeGet(MlirContext ctx,
uint32_t width);
/// Returns the width of the RTG immediate type.
MLIR_CAPI_EXPORTED uint32_t rtgImmediateTypeGetWidth(MlirType type);
//===----------------------------------------------------------------------===//
// Attribute API.
//===----------------------------------------------------------------------===//
@ -108,6 +118,21 @@ MLIR_CAPI_EXPORTED bool rtgAttrIsADefaultContextAttr(MlirAttribute attr);
MLIR_CAPI_EXPORTED MlirAttribute rtgDefaultContextAttrGet(MlirContext ctxt,
MlirType type);
/// Checks if the attribute is an RTG immediate attribute.
MLIR_CAPI_EXPORTED bool rtgAttrIsAImmediate(MlirAttribute attr);
/// Creates an RTG immediate attribute in the context with the given width and
/// value.
MLIR_CAPI_EXPORTED MlirAttribute rtgImmediateAttrGet(MlirContext ctx,
uint32_t width,
uint64_t value);
/// Returns the width of the RTG immediate attribute.
MLIR_CAPI_EXPORTED uint32_t rtgImmediateAttrGetWidth(MlirAttribute attr);
/// Returns the value of the RTG immediate attribute.
MLIR_CAPI_EXPORTED uint64_t rtgImmediateAttrGetValue(MlirAttribute attr);
#ifdef __cplusplus
}
#endif

View File

@ -10,6 +10,7 @@
#define CIRCT_DIALECT_RTG_IR_RTGATTRIBUTES_H
#include "circt/Dialect/RTG/IR/RTGAttrInterfaces.h"
#include "circt/Dialect/RTG/IR/RTGTypes.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/BuiltinAttributes.h"

View File

@ -75,4 +75,25 @@ def DefaultContextAttr : RTGAttrDef<"DefaultContext", [
let assemblyFormat = "";
}
//===----------------------------------------------------------------------===//
// Attributes for ISA targets
//===----------------------------------------------------------------------===//
class RTGISAAttrDef<string name, list<Trait> traits = []>
: RTGAttrDef<name, traits> { let mnemonic = "isa." # !tolower(name); }
def ImmediateAttr : RTGISAAttrDef<"Immediate", [
DeclareAttrInterfaceMethods<TypedAttrInterface>,
]> {
let summary = "an ISA immediate value";
let description = [{
This represents an ISA immediate of arbitrary but fixed bit-width. The type
of this attribute must always be an `ImmediateType` of matching bit-width.
}];
let parameters = (ins "llvm::APInt":$value);
let hasCustomAssemblyFormat = true;
}
#endif // CIRCT_DIALECT_RTG_IR_RTGATTRIBUTES_TD

View File

@ -29,6 +29,8 @@ def RTGDialect : Dialect {
let useDefaultTypePrinterParser = 1;
let cppNamespace = "::circt::rtg";
let hasConstantMaterializer = 1;
let extraClassDeclaration = [{
void registerAttributes();
void registerTypes();

View File

@ -25,6 +25,21 @@ include "circt/Dialect/RTG/IR/RTGISAAssemblyInterfaces.td"
class RTGOp<string mnemonic, list<Trait> traits = []> :
Op<RTGDialect, mnemonic, traits>;
def ConstantOp : RTGOp<"constant", [
Pure,
ConstantLike,
DeclareOpInterfaceMethods<InferTypeOpInterface>
]> {
let summary = "create an SSA value from an attribute";
let arguments = (ins TypedAttrInterface:$value);
let results = (outs AnyType:$result);
let assemblyFormat = "$value attr-dict";
let hasFolder = 1;
}
//===- Sequence Handling Operations ---------------------------------------===//
def SequenceOp : RTGOp<"sequence", [

View File

@ -16,7 +16,12 @@
include "circt/Dialect/RTG/IR/RTGDialect.td"
include "mlir/IR/AttrTypeBase.td"
class RTGTypeDef<string name> : TypeDef<RTGDialect, name>;
class RTGTypeDef<string name, list<Trait> traits = []>
: TypeDef<RTGDialect, name, traits>;
//===----------------------------------------------------------------------===//
// Sequence Types
//===----------------------------------------------------------------------===//
def SequenceType : RTGTypeDef<"Sequence"> {
let summary = "handle to a sequence or sequence family";
@ -51,17 +56,9 @@ def FullySubstitutedSequenceType : DialectType<RTGDialect,
"::circt::rtg::SequenceType::get($_builder.getContext(), " #
"llvm::ArrayRef<::mlir::Type>{})">;
def LabelType : RTGTypeDef<"Label"> {
let summary = "a reference to a label";
let description = [{
This type represents a label. Payload dialects can add operations to cast
from this type to an immediate type they can use as an operand to an
instruction.
}];
let mnemonic = "label";
let assemblyFormat = "";
}
//===----------------------------------------------------------------------===//
// Types for common datastructures
//===----------------------------------------------------------------------===//
def SetType : RTGTypeDef<"Set"> {
let summary = "a set of values";
@ -123,6 +120,43 @@ def DictType : RTGTypeDef<"Dict"> {
let genVerifyDecl = 1;
}
//===----------------------------------------------------------------------===//
// Types for ISA targets
//===----------------------------------------------------------------------===//
class RTGISATypeDef<string name, list<Trait> traits = []>
: RTGTypeDef<name, traits> { let mnemonic = "isa." # !tolower(name); }
def LabelType : RTGISATypeDef<"Label"> {
let summary = "a reference to a label";
let description = [{
This type represents a label. Payload dialects can add operations to cast
from this type to an immediate type they can use as an operand to an
instruction or allow an operand of this type directly.
}];
let assemblyFormat = "";
}
def ImmediateType : RTGISATypeDef<"Immediate"> {
let summary = "an ISA immediate";
let description = [{
This type represents immediates of arbitrary but fixed bit-width.
The RTG dialect provides this type to avoid duplication in ISA payload
dialects.
}];
let parameters = (ins "uint32_t":$width);
let assemblyFormat = "`<` $width `>`";
}
class ImmediateOfWidth<int width> : Type<
And<[CPred<"llvm::isa<::circt::rtg::ImmediateType>($_self)">,
CPred<"llvm::cast<::circt::rtg::ImmediateType>($_self).getWidth() == " # width>]>,
"a " # width # "-bit immediate">,
BuildableType<"::circt::rtg::ImmediateType::get($_builder.getContext(), " # width # ")">;
class ImmTypeBase<int width> : TypeDef<RTGDialect, "Imm" # width, []> {
let summary = "represents a " # width # "-bit immediate";
let mnemonic = "imm" # width;

View File

@ -32,6 +32,8 @@ public:
auto *thisCast = static_cast<ConcreteType *>(this);
return TypeSwitch<Operation *, ResultType>(op)
.template Case<
// Constants
ConstantOp,
// Bags
BagCreateOp, BagSelectRandomOp, BagDifferenceOp, BagUnionOp,
BagUniqueSizeOp,
@ -82,6 +84,7 @@ public:
return static_cast<ConcreteType *>(this)->visit##OPKIND##Op(op, args...); \
}
HANDLE(ConstantOp, Unhandled);
HANDLE(SequenceOp, Unhandled);
HANDLE(GetSequenceOp, Unhandled);
HANDLE(SubstituteSequenceOp, Unhandled);
@ -120,10 +123,11 @@ public:
ResultType dispatchTypeVisitor(Type type, ExtraArgs... args) {
auto *thisCast = static_cast<ConcreteType *>(this);
return TypeSwitch<Type, ResultType>(type)
.template Case<SequenceType, SetType, BagType, DictType, LabelType,
IndexType, IntegerType>([&](auto expr) -> ResultType {
return thisCast->visitType(expr, args...);
})
.template Case<ImmediateType, SequenceType, SetType, BagType, DictType,
LabelType, IndexType, IntegerType>(
[&](auto expr) -> ResultType {
return thisCast->visitType(expr, args...);
})
.template Case<ContextResourceTypeInterface>(
[&](auto expr) -> ResultType {
return thisCast->visitContextResourceType(expr, args...);
@ -163,6 +167,7 @@ public:
args...); \
}
HANDLE(ImmediateType, Unhandled);
HANDLE(SequenceType, Unhandled);
HANDLE(SetType, Unhandled);
HANDLE(BagType, Unhandled);

View File

@ -106,7 +106,7 @@ with Context() as ctx, Location.unknown():
# CHECK: index{{$}}
print(bagTy.element_type)
# CHECK: rtg.sequence @seq(%{{.*}}: !rtg.sequence, %{{.*}}: !rtg.label, %{{.*}}: !rtg.set<index>, %{{.*}}: !rtg.bag<index>, %{{.*}}: !rtgtest.ireg, %{{.*}}: !rtg.randomized_sequence)
# CHECK: rtg.sequence @seq(%{{.*}}: !rtg.sequence, %{{.*}}: !rtg.isa.label, %{{.*}}: !rtg.set<index>, %{{.*}}: !rtg.bag<index>, %{{.*}}: !rtgtest.ireg, %{{.*}}: !rtg.randomized_sequence)
print(m)
with Context() as ctx, Location.unknown():
@ -221,3 +221,17 @@ with Context() as ctx, Location.unknown():
print(attr.type)
# CHECK: #rtg.default : !rtgtest.cpu
print(attr)
immediate_type = rtg.ImmediateType.get(32)
# CHECK: width=32
print(f"width={immediate_type.width}")
# CHECK: !rtg.isa.immediate<32>
print(immediate_type)
immediate_attr = rtg.ImmediateAttr.get(32, 42)
# CHECK: width=32
print(f"width={immediate_attr.width}")
# CHECK: value=42
print(f"value={immediate_attr.value}")
# CHECK: #rtg.isa.immediate<32, 42>
print(immediate_attr)

View File

@ -23,6 +23,13 @@ using namespace mlir::python::nanobind_adaptors;
void circt::python::populateDialectRTGSubmodule(nb::module_ &m) {
m.doc() = "RTG dialect Python native extension";
//===--------------------------------------------------------------------===//
// Types
//===--------------------------------------------------------------------===//
// Sequence Types
//===--------------------------------------------------------------------===//
mlir_type_subclass(m, "SequenceType", rtgTypeIsASequence)
.def_classmethod(
"get",
@ -48,13 +55,8 @@ void circt::python::populateDialectRTGSubmodule(nb::module_ &m) {
},
nb::arg("self"), nb::arg("ctxt") = nullptr);
mlir_type_subclass(m, "LabelType", rtgTypeIsALabel)
.def_classmethod(
"get",
[](nb::object cls, MlirContext ctxt) {
return cls(rtgLabelTypeGet(ctxt));
},
nb::arg("self"), nb::arg("ctxt") = nullptr);
// Common Datastructure Types
//===--------------------------------------------------------------------===//
mlir_type_subclass(m, "SetType", rtgTypeIsASet)
.def_classmethod(
@ -98,6 +100,43 @@ void circt::python::populateDialectRTGSubmodule(nb::module_ &m) {
std::vector<std::pair<MlirAttribute, MlirType>>(),
nb::arg("ctxt") = nullptr);
// Types for ISA targets
//===--------------------------------------------------------------------===//
mlir_type_subclass(m, "LabelType", rtgTypeIsALabel)
.def_classmethod(
"get",
[](nb::object cls, MlirContext ctxt) {
return cls(rtgLabelTypeGet(ctxt));
},
nb::arg("self"), nb::arg("ctxt") = nullptr);
mlir_type_subclass(m, "ImmediateType", rtgTypeIsAImmediate)
.def_classmethod(
"get",
[](nb::object cls, uint32_t width, MlirContext ctx) {
return cls(rtgImmediateTypeGet(ctx, width));
},
nb::arg("self"), nb::arg("width"), nb::arg("ctx") = nullptr)
.def_property_readonly("width", [](MlirType self) {
return rtgImmediateTypeGetWidth(self);
});
//===--------------------------------------------------------------------===//
// Attributes
//===--------------------------------------------------------------------===//
mlir_attribute_subclass(m, "DefaultContextAttr", rtgAttrIsADefaultContextAttr)
.def_classmethod(
"get",
[](nb::object cls, MlirType type, MlirContext ctxt) {
return cls(rtgDefaultContextAttrGet(ctxt, type));
},
nb::arg("self"), nb::arg("type"), nb::arg("ctxt") = nullptr);
// Attributes for ISA targets
//===--------------------------------------------------------------------===//
nb::enum_<RTGLabelVisibility>(m, "LabelVisibility")
.value("LOCAL", RTG_LABEL_VISIBILITY_LOCAL)
.value("GLOBAL", RTG_LABEL_VISIBILITY_GLOBAL)
@ -116,11 +155,18 @@ void circt::python::populateDialectRTGSubmodule(nb::module_ &m) {
return rtgLabelVisibilityAttrGetValue(self);
});
mlir_attribute_subclass(m, "DefaultContextAttr", rtgAttrIsADefaultContextAttr)
mlir_attribute_subclass(m, "ImmediateAttr", rtgAttrIsAImmediate)
.def_classmethod(
"get",
[](nb::object cls, MlirType type, MlirContext ctxt) {
return cls(rtgDefaultContextAttrGet(ctxt, type));
[](nb::object cls, uint32_t width, uint64_t value, MlirContext ctx) {
return cls(rtgImmediateAttrGet(ctx, width, value));
},
nb::arg("self"), nb::arg("type"), nb::arg("ctxt") = nullptr);
nb::arg("self"), nb::arg("width"), nb::arg("value"),
nb::arg("ctx") = nullptr)
.def_property_readonly(
"width",
[](MlirAttribute self) { return rtgImmediateAttrGetWidth(self); })
.def_property_readonly("value", [](MlirAttribute self) {
return rtgImmediateAttrGetValue(self);
});
}

View File

@ -115,10 +115,39 @@ MlirType rtgDictTypeGet(MlirContext ctxt, intptr_t numEntries,
return wrap(DictType::get(unwrap(ctxt), entries));
}
// ImmediateType
//===----------------------------------------------------------------------===//
bool rtgTypeIsAImmediate(MlirType type) {
return isa<ImmediateType>(unwrap(type));
}
MlirType rtgImmediateTypeGet(MlirContext ctx, uint32_t width) {
return wrap(ImmediateType::get(unwrap(ctx), width));
}
uint32_t rtgImmediateTypeGetWidth(MlirType type) {
return cast<ImmediateType>(unwrap(type)).getWidth();
}
//===----------------------------------------------------------------------===//
// Attribute API.
//===----------------------------------------------------------------------===//
// DefaultContext
//===----------------------------------------------------------------------===//
bool rtgAttrIsADefaultContextAttr(MlirAttribute attr) {
return isa<DefaultContextAttr>(unwrap(attr));
}
MlirAttribute rtgDefaultContextAttrGet(MlirContext ctxt, MlirType type) {
return wrap(DefaultContextAttr::get(unwrap(ctxt), unwrap(type)));
}
// Label Visibility
//===----------------------------------------------------------------------===//
bool rtgAttrIsALabelVisibilityAttr(MlirAttribute attr) {
return isa<LabelVisibilityAttr>(unwrap(attr));
}
@ -152,10 +181,22 @@ MlirAttribute rtgLabelVisibilityAttrGet(MlirContext ctxt,
return wrap(LabelVisibilityAttr::get(unwrap(ctxt), convert(visibility)));
}
bool rtgAttrIsADefaultContextAttr(MlirAttribute attr) {
return isa<DefaultContextAttr>(unwrap(attr));
// ImmediateAttr
//===----------------------------------------------------------------------===//
bool rtgAttrIsAImmediate(MlirAttribute attr) {
return isa<ImmediateAttr>(unwrap(attr));
}
MlirAttribute rtgDefaultContextAttrGet(MlirContext ctxt, MlirType type) {
return wrap(DefaultContextAttr::get(unwrap(ctxt), unwrap(type)));
MlirAttribute rtgImmediateAttrGet(MlirContext ctx, uint32_t width,
uint64_t value) {
return wrap(rtg::ImmediateAttr::get(unwrap(ctx), APInt(width, value)));
}
uint32_t rtgImmediateAttrGetWidth(MlirAttribute attr) {
return cast<ImmediateAttr>(unwrap(attr)).getValue().getBitWidth();
}
uint64_t rtgImmediateAttrGetValue(MlirAttribute attr) {
return cast<ImmediateAttr>(unwrap(attr)).getValue().getZExtValue();
}

View File

@ -15,6 +15,60 @@
using namespace circt;
using namespace rtg;
//===----------------------------------------------------------------------===//
// ImmediateAttr
//===----------------------------------------------------------------------===//
Type ImmediateAttr::getType() const {
return ImmediateType::get(getContext(), getValue().getBitWidth());
}
Attribute ImmediateAttr::parse(AsmParser &odsParser, Type odsType) {
llvm::SMLoc loc = odsParser.getCurrentLocation();
APInt val;
uint32_t width; // NOTE: this integer type should match the 'width' parameter
// type in immediate type.
if (odsParser.parseLess() || odsParser.parseInteger(width) ||
odsParser.parseComma() || odsParser.parseInteger(val) ||
odsParser.parseGreater())
return {};
// If the attribute type is explicitly given, check that the bit-widths match.
if (auto immTy = llvm::dyn_cast_or_null<ImmediateType>(odsType)) {
if (immTy.getWidth() != width) {
odsParser.emitError(loc) << "explicit immediate type bit-width does not "
"match attribute bit-width, "
<< immTy.getWidth() << " vs " << width;
return {};
}
}
if (width > val.getBitWidth()) {
// sext is always safe here, even for unsigned values, because the
// parseOptionalInteger method will return something with a zero in the
// top bits if it is a positive number.
val = val.sext(width);
} else if (width < val.getBitWidth()) {
// The parser can return an unnecessarily wide result.
// This isn't a problem, but truncating off bits is bad.
unsigned neededBits =
val.isNegative() ? val.getSignificantBits() : val.getActiveBits();
if (width < neededBits) {
odsParser.emitError(loc)
<< "integer value out-of-range for bit-width " << width;
return {};
}
val = val.trunc(width);
}
return ImmediateAttr::get(odsParser.getContext(), val);
}
void ImmediateAttr::print(AsmPrinter &odsPrinter) const {
odsPrinter << "<" << getValue().getBitWidth() << ", " << getValue() << ">";
}
//===----------------------------------------------------------------------===//
// TableGen generated logic.
//===----------------------------------------------------------------------===//

View File

@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "circt/Dialect/RTG/IR/RTGDialect.h"
#include "circt/Dialect/RTG/IR/RTGAttributes.h"
#include "circt/Dialect/RTG/IR/RTGOps.h"
#include "circt/Dialect/RTG/IR/RTGTypes.h"
#include "mlir/IR/Builders.h"
@ -34,6 +35,22 @@ void RTGDialect::initialize() {
>();
}
/// Registered hook to materialize a single constant operation from a given
/// attribute value with the desired resultant type. This method should use
/// the provided builder to create the operation without changing the
/// insertion position. The generated operation is expected to be constant
/// like, i.e. single result, zero operands, non side-effecting, etc. On
/// success, this hook should return the value generated to represent the
/// constant value. Otherwise, it should return null on failure.
Operation *RTGDialect::materializeConstant(OpBuilder &builder, Attribute value,
Type type, Location loc) {
if (auto attr = dyn_cast<ImmediateAttr>(value))
if (type == attr.getType())
return builder.create<ConstantOp>(loc, attr);
return nullptr;
}
#include "circt/Dialect/RTG/IR/RTGEnums.cpp.inc"
#include "circt/Dialect/RTG/IR/RTGDialect.cpp.inc"

View File

@ -20,6 +20,22 @@ using namespace mlir;
using namespace circt;
using namespace rtg;
//===----------------------------------------------------------------------===//
// ConstantOp
//===----------------------------------------------------------------------===//
LogicalResult
ConstantOp::inferReturnTypes(MLIRContext *context, std::optional<Location> loc,
ValueRange operands, DictionaryAttr attributes,
OpaqueProperties properties, RegionRange regions,
SmallVectorImpl<Type> &inferredReturnTypes) {
inferredReturnTypes.push_back(
properties.as<Properties *>()->getValue().getType());
return success();
}
OpFoldResult ConstantOp::fold(FoldAdaptor adaptor) { return getValueAttr(); }
//===----------------------------------------------------------------------===//
// SequenceOp
//===----------------------------------------------------------------------===//

View File

@ -874,6 +874,10 @@ public:
return visitUnhandledOp(op);
}
FailureOr<DeletionKind> visitOp(ConstantOp op) {
return visitConstantLike(op);
}
FailureOr<DeletionKind> visitOp(GetSequenceOp op) {
SmallVector<ElaboratorValue> replacements;
state[op.getResult()] =

View File

@ -52,7 +52,7 @@ static void testLabelType(MlirContext ctx) {
// CHECK: is_label
fprintf(stderr, rtgTypeIsALabel(labelTy) ? "is_label\n" : "isnot_label\n");
// CHECK: !rtg.label
// CHECK: !rtg.isa.label
mlirTypeDump(labelTy);
}
@ -138,6 +138,29 @@ static void testDefaultContextAttr(MlirContext ctx) {
mlirTypeDump(mlirAttributeGetType(defaultCtxtAttr));
}
static void testImmediate(MlirContext ctx) {
MlirType immediateTy = rtgImmediateTypeGet(ctx, 32);
// CHECK: is_immediate
fprintf(stderr, rtgTypeIsAImmediate(immediateTy) ? "is_immediate\n"
: "isnot_immediate\n");
// CHECK: !rtg.isa.immediate<32>
mlirTypeDump(immediateTy);
// CHECK: width=32
fprintf(stderr, "width=%u\n", rtgImmediateTypeGetWidth(immediateTy));
MlirAttribute immediateAttr = rtgImmediateAttrGet(ctx, 32, 42);
// CHECK: is_immediate_attr
fprintf(stderr, rtgAttrIsAImmediate(immediateAttr)
? "is_immediate_attr\n"
: "isnot_immediate_attr\n");
// CHECK: #rtg.isa.immediate<32, 42>
mlirAttributeDump(immediateAttr);
// CHECK: width=32
fprintf(stderr, "width=%u\n", rtgImmediateAttrGetWidth(immediateAttr));
// CHECK: value=42
fprintf(stderr, "value=%llu\n", rtgImmediateAttrGetValue(immediateAttr));
}
int main(int argc, char **argv) {
MlirContext ctx = mlirContextCreate();
mlirDialectHandleLoadDialect(mlirGetDialectHandle__rtg__(), ctx);
@ -152,6 +175,7 @@ int main(int argc, char **argv) {
testLabelVisibilityAttr(ctx);
testDefaultContextAttr(ctx);
testImmediate(ctx);
mlirContextDestroy(ctx);

View File

@ -1,5 +1,11 @@
// RUN: circt-opt %s --verify-roundtrip | FileCheck %s
// CHECK-LABEL: @constants
rtg.test @constants() {
// CHECK-NEXT: rtg.constant #rtg.isa.immediate<2, -1> : !rtg.isa.immediate<2>
%0 = rtg.constant #rtg.isa.immediate<2, -1>
}
// CHECK-LABEL: rtg.sequence @ranomizedSequenceType
// CHECK-SAME: (%{{.*}}: !rtg.randomized_sequence)
rtg.sequence @ranomizedSequenceType(%seq: !rtg.randomized_sequence) {}
@ -20,8 +26,8 @@ rtg.sequence @seq0() {
}
// CHECK-LABEL: rtg.sequence @seqAttrsAndTypeElements
// CHECK-SAME: (%arg0: !rtg.sequence<!rtg.sequence<!rtg.label, !rtg.set<index>>>) attributes {rtg.some_attr} {
rtg.sequence @seqAttrsAndTypeElements(%arg0: !rtg.sequence<!rtg.sequence<!rtg.label, !rtg.set<index>>>) attributes {rtg.some_attr} {}
// CHECK-SAME: (%arg0: !rtg.sequence<!rtg.sequence<!rtg.isa.label, !rtg.set<index>>>) attributes {rtg.some_attr} {
rtg.sequence @seqAttrsAndTypeElements(%arg0: !rtg.sequence<!rtg.sequence<!rtg.isa.label, !rtg.set<index>>>) attributes {rtg.some_attr} {}
// CHECK-LABEL: rtg.sequence @seq1
// CHECK-SAME: (%arg0: i32, %arg1: !rtg.sequence)

View File

@ -1,5 +1,18 @@
// RUN: circt-opt %s --split-input-file --verify-diagnostics
rtg.test @constantTooBig() {
// expected-error @below {{integer value out-of-range for bit-width 2}}
rtg.constant #rtg.isa.immediate<2, 4>
}
// -----
rtg.test @immediateWidthMismatch() {
// expected-error @below {{explicit immediate type bit-width does not match attribute bit-width, 1 vs 2}}
rtg.constant #rtg.isa.immediate<2, 1> : !rtg.isa.immediate<1>
}
// -----
func.func @seq0() {
return

View File

@ -5,6 +5,15 @@ func.func @dummy2(%arg0: index) -> () {return}
func.func @dummy3(%arg0: !rtg.sequence) -> () {return}
func.func @dummy4(%arg0: index, %arg1: index, %arg2: !rtg.bag<index>, %arg3: !rtg.bag<index>) -> () {return}
func.func @dummy5(%arg0: i1) -> () {return}
func.func @dummy6(%arg0: !rtg.isa.immediate<2>) -> () {return}
// CHECK-LABEL: @constantImmediate
rtg.test @constantImmediate() {
// CHECK-NEXT: [[V0:%.+]] = rtg.constant #rtg.isa.immediate<2, -1>
// CHECK-NEXT: func.call @dummy6([[V0]]) : (!rtg.isa.immediate<2>) -> ()
%0 = rtg.constant #rtg.isa.immediate<2, -1>
func.call @dummy6(%0) : (!rtg.isa.immediate<2>) -> ()
}
// Test the set operations and passing a sequence to another one via argument
// CHECK-LABEL: rtg.test @setOperations
@ -574,17 +583,17 @@ rtg.test @contextSwitchNotAvailable(cpu = %cpu: !rtgtest.cpu) {
// -----
rtg.test @emptySetSelect() {
%0 = rtg.set_create : !rtg.label
%0 = rtg.set_create : !rtg.isa.label
// expected-error @below {{cannot select from an empty set}}
%1 = rtg.set_select_random %0 : !rtg.set<!rtg.label>
%1 = rtg.set_select_random %0 : !rtg.set<!rtg.isa.label>
rtg.label local %1
}
// -----
rtg.test @emptyBagSelect() {
%0 = rtg.bag_create : !rtg.label
%0 = rtg.bag_create : !rtg.isa.label
// expected-error @below {{cannot select from an empty bag}}
%1 = rtg.bag_select_random %0 : !rtg.bag<!rtg.label>
%1 = rtg.bag_select_random %0 : !rtg.bag<!rtg.isa.label>
rtg.label local %1
}

View File

@ -6,7 +6,7 @@ rtg.test @test0() {
%label = rtg.label_decl "label_name"
// expected-error @below {{labels cannot be emitted as binary}}
rtgtest.rv32i.beq %rd, %rs, %label : !rtg.label
rtgtest.rv32i.beq %rd, %rs, %label : !rtg.isa.label
}
// -----

View File

@ -9,7 +9,7 @@ rtg.test @test0() {
%label = rtg.label_decl "label_name"
// CHECK-NEXT: beq ra, s0, label_name
rtgtest.rv32i.beq %rd, %rs, %label : !rtg.label
rtgtest.rv32i.beq %rd, %rs, %label : !rtg.isa.label
// CHECK-NEXT:label_name:
rtg.label local %label
// CHECK-NEXT:.extern label_name
@ -18,21 +18,21 @@ rtg.test @test0() {
// CHECK-NEXT:label_name:
rtg.label global %label
// CHECK-NEXT: bne ra, s0, label_name
rtgtest.rv32i.bne %rd, %rs, %label : !rtg.label
rtgtest.rv32i.bne %rd, %rs, %label : !rtg.isa.label
// CHECK-NEXT: blt ra, s0, label_name
rtgtest.rv32i.blt %rd, %rs, %label : !rtg.label
rtgtest.rv32i.blt %rd, %rs, %label : !rtg.isa.label
// CHECK-NEXT: bge ra, s0, label_name
rtgtest.rv32i.bge %rd, %rs, %label : !rtg.label
rtgtest.rv32i.bge %rd, %rs, %label : !rtg.isa.label
// CHECK-NEXT: bltu ra, s0, label_name
rtgtest.rv32i.bltu %rd, %rs, %label : !rtg.label
rtgtest.rv32i.bltu %rd, %rs, %label : !rtg.isa.label
// CHECK-NEXT: bgeu ra, s0, label_name
rtgtest.rv32i.bgeu %rd, %rs, %label : !rtg.label
rtgtest.rv32i.bgeu %rd, %rs, %label : !rtg.isa.label
// CHECK-NEXT: lui ra, label_name
rtgtest.rv32i.lui %rd, %label : !rtg.label
rtgtest.rv32i.lui %rd, %label : !rtg.isa.label
// CHECK-NEXT: auipc ra, label_name
rtgtest.rv32i.auipc %rd, %label : !rtg.label
rtgtest.rv32i.auipc %rd, %label : !rtg.isa.label
// CHECK-NEXT: jal ra, label_name
rtgtest.rv32i.jal %rd, %label : !rtg.label
rtgtest.rv32i.jal %rd, %label : !rtg.isa.label
}
// CHECK-EMPTY:

View File

@ -100,8 +100,8 @@ rtg.test @immediates() {
}
// CHECK-LABEL: @instructions
// CHECK-SAME: (imm = [[IMM:%.+]]: !rtgtest.imm12, imm13 = [[IMM13:%.+]]: !rtgtest.imm13, imm21 = [[IMM21:%.+]]: !rtgtest.imm21, imm32 = [[IMM32:%.+]]: !rtgtest.imm32, imm5 = [[IMM5:%.+]]: !rtgtest.imm5, label = [[LABEL:%.+]]: !rtg.label, rd = [[RD:%.+]]: !rtgtest.ireg, rs = [[RS:%.+]]: !rtgtest.ireg)
rtg.test @instructions(imm = %imm: !rtgtest.imm12, imm13 = %imm13: !rtgtest.imm13, imm21 = %imm21: !rtgtest.imm21, imm32 = %imm32: !rtgtest.imm32, imm5 = %imm5: !rtgtest.imm5, label = %label: !rtg.label, rd = %rd: !rtgtest.ireg, rs = %rs: !rtgtest.ireg) {
// CHECK-SAME: (imm = [[IMM:%.+]]: !rtgtest.imm12, imm13 = [[IMM13:%.+]]: !rtgtest.imm13, imm21 = [[IMM21:%.+]]: !rtgtest.imm21, imm32 = [[IMM32:%.+]]: !rtgtest.imm32, imm5 = [[IMM5:%.+]]: !rtgtest.imm5, label = [[LABEL:%.+]]: !rtg.isa.label, rd = [[RD:%.+]]: !rtgtest.ireg, rs = [[RS:%.+]]: !rtgtest.ireg)
rtg.test @instructions(imm = %imm: !rtgtest.imm12, imm13 = %imm13: !rtgtest.imm13, imm21 = %imm21: !rtgtest.imm21, imm32 = %imm32: !rtgtest.imm32, imm5 = %imm5: !rtgtest.imm5, label = %label: !rtg.isa.label, rd = %rd: !rtgtest.ireg, rs = %rs: !rtgtest.ireg) {
// CHECK: rtgtest.rv32i.jalr [[RD]], [[RS]], [[IMM]]
rtgtest.rv32i.jalr %rd, %rs, %imm
// CHECK: rtgtest.rv32i.lb [[RD]], [[RS]], [[IMM]]
@ -130,18 +130,18 @@ rtg.test @instructions(imm = %imm: !rtgtest.imm12, imm13 = %imm13: !rtgtest.imm1
rtgtest.rv32i.bltu %rd, %rs, %imm13 : !rtgtest.imm13
// CHECK: rtgtest.rv32i.bgeu [[RD]], [[RS]], [[IMM13]] : !rtgtest.imm13
rtgtest.rv32i.bgeu %rd, %rs, %imm13 : !rtgtest.imm13
// CHECK: rtgtest.rv32i.beq [[RD]], [[RS]], [[LABEL]] : !rtg.label
rtgtest.rv32i.beq %rd, %rs, %label : !rtg.label
// CHECK: rtgtest.rv32i.bne [[RD]], [[RS]], [[LABEL]] : !rtg.label
rtgtest.rv32i.bne %rd, %rs, %label : !rtg.label
// CHECK: rtgtest.rv32i.blt [[RD]], [[RS]], [[LABEL]] : !rtg.label
rtgtest.rv32i.blt %rd, %rs, %label : !rtg.label
// CHECK: rtgtest.rv32i.bge [[RD]], [[RS]], [[LABEL]] : !rtg.label
rtgtest.rv32i.bge %rd, %rs, %label : !rtg.label
// CHECK: rtgtest.rv32i.bltu [[RD]], [[RS]], [[LABEL]] : !rtg.label
rtgtest.rv32i.bltu %rd, %rs, %label : !rtg.label
// CHECK: rtgtest.rv32i.bgeu [[RD]], [[RS]], [[LABEL]] : !rtg.label
rtgtest.rv32i.bgeu %rd, %rs, %label : !rtg.label
// CHECK: rtgtest.rv32i.beq [[RD]], [[RS]], [[LABEL]] : !rtg.isa.label
rtgtest.rv32i.beq %rd, %rs, %label : !rtg.isa.label
// CHECK: rtgtest.rv32i.bne [[RD]], [[RS]], [[LABEL]] : !rtg.isa.label
rtgtest.rv32i.bne %rd, %rs, %label : !rtg.isa.label
// CHECK: rtgtest.rv32i.blt [[RD]], [[RS]], [[LABEL]] : !rtg.isa.label
rtgtest.rv32i.blt %rd, %rs, %label : !rtg.isa.label
// CHECK: rtgtest.rv32i.bge [[RD]], [[RS]], [[LABEL]] : !rtg.isa.label
rtgtest.rv32i.bge %rd, %rs, %label : !rtg.isa.label
// CHECK: rtgtest.rv32i.bltu [[RD]], [[RS]], [[LABEL]] : !rtg.isa.label
rtgtest.rv32i.bltu %rd, %rs, %label : !rtg.isa.label
// CHECK: rtgtest.rv32i.bgeu [[RD]], [[RS]], [[LABEL]] : !rtg.isa.label
rtgtest.rv32i.bgeu %rd, %rs, %label : !rtg.isa.label
// CHECK: rtgtest.rv32i.add [[RD]], [[RS]], [[RS]] {rtg.some_attr}
rtgtest.rv32i.add %rd, %rs, %rs {rtg.some_attr}
@ -178,12 +178,12 @@ rtg.test @instructions(imm = %imm: !rtgtest.imm12, imm13 = %imm13: !rtgtest.imm1
// CHECK: rtgtest.rv32i.jal [[RD]], [[IMM21]] : !rtgtest.imm21 {rtg.some_attr}
rtgtest.rv32i.jal %rd, %imm21 : !rtgtest.imm21 {rtg.some_attr}
// CHECK: rtgtest.rv32i.lui [[RD]], [[LABEL]] : !rtg.label {rtg.some_attr}
rtgtest.rv32i.lui %rd, %label : !rtg.label {rtg.some_attr}
// CHECK: rtgtest.rv32i.auipc [[RD]], [[LABEL]] : !rtg.label {rtg.some_attr}
rtgtest.rv32i.auipc %rd, %label : !rtg.label {rtg.some_attr}
// CHECK: rtgtest.rv32i.jal [[RD]], [[LABEL]] : !rtg.label {rtg.some_attr}
rtgtest.rv32i.jal %rd, %label : !rtg.label {rtg.some_attr}
// CHECK: rtgtest.rv32i.lui [[RD]], [[LABEL]] : !rtg.isa.label {rtg.some_attr}
rtgtest.rv32i.lui %rd, %label : !rtg.isa.label {rtg.some_attr}
// CHECK: rtgtest.rv32i.auipc [[RD]], [[LABEL]] : !rtg.isa.label {rtg.some_attr}
rtgtest.rv32i.auipc %rd, %label : !rtg.isa.label {rtg.some_attr}
// CHECK: rtgtest.rv32i.jal [[RD]], [[LABEL]] : !rtg.isa.label {rtg.some_attr}
rtgtest.rv32i.jal %rd, %label : !rtg.isa.label {rtg.some_attr}
// CHECK: rtgtest.rv32i.addi [[RD]], [[RS]], [[IMM]] {rtg.some_attr}
rtgtest.rv32i.addi %rd, %rs, %imm {rtg.some_attr}

View File

@ -3,5 +3,6 @@ add_subdirectory(FIRRTL)
add_subdirectory(ESI)
add_subdirectory(HW)
add_subdirectory(OM)
add_subdirectory(RTG)
add_subdirectory(RTGTest)
add_subdirectory(SMT)

View File

@ -0,0 +1,9 @@
add_circt_unittest(CIRCTRTGTests
MaterializerTest.cpp
)
target_link_libraries(CIRCTRTGTests
PRIVATE
CIRCTRTGDialect
MLIRIR
)

View File

@ -0,0 +1,39 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "circt/Dialect/RTG/IR/RTGAttributes.h"
#include "circt/Dialect/RTG/IR/RTGDialect.h"
#include "circt/Dialect/RTG/IR/RTGOps.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinOps.h"
#include "gtest/gtest.h"
using namespace mlir;
using namespace circt;
using namespace rtg;
namespace {
TEST(MaterializerTest, ImmediateAttr) {
MLIRContext context;
context.loadDialect<RTGDialect>();
Location loc(UnknownLoc::get(&context));
auto moduleOp = ModuleOp::create(loc);
OpBuilder builder = OpBuilder::atBlockBegin(moduleOp.getBody());
auto attr = ImmediateAttr::get(&context, APInt(12, 0));
auto *op0 = context.getLoadedDialect<RTGDialect>()->materializeConstant(
builder, attr, attr.getType(), loc);
auto *op1 = context.getLoadedDialect<RTGDialect>()->materializeConstant(
builder, attr, ImmediateType::get(&context, 2), loc);
ASSERT_TRUE(op0 && isa<ConstantOp>(op0));
ASSERT_EQ(op1, nullptr);
}
} // namespace