mirror of https://github.com/llvm/circt.git
[FIRRTL] Map: MapCreateOp, parsing support. (#5962)
Add operation for creating Map's, add parsing support and tests.
This commit is contained in:
parent
806b4153b4
commit
81678d3e7d
|
@ -1170,6 +1170,24 @@ def ListCreateOp : FIRRTLOp<"list.create", [Pure, SameTypeOperands]> {
|
|||
let hasVerifier = 1;
|
||||
}
|
||||
|
||||
def MapCreateOp : FIRRTLOp<"map.create", [Pure, SameVariadicOperandSize]> {
|
||||
let summary = "Produce a map value";
|
||||
let description = [{
|
||||
Produces a value of map type containing the provided key/value elements.
|
||||
|
||||
Example:
|
||||
```mlir
|
||||
%4 = firrtl.map.create (%0 -> %1, %2 -> %3) : !firrtl.map<string, integer>
|
||||
```
|
||||
}];
|
||||
|
||||
let arguments = (ins Variadic<PropertyType>:$keys, Variadic<PropertyType>:$values);
|
||||
let results = (outs MapType:$result);
|
||||
|
||||
let hasCustomAssemblyFormat = 1;
|
||||
let hasVerifier = 1;
|
||||
}
|
||||
|
||||
def BoolConstantOp : FIRRTLOp<"bool", [Pure, ConstantLike]> {
|
||||
let summary = "Produce a constant boolean value";
|
||||
let description = [{
|
||||
|
|
|
@ -62,7 +62,7 @@ public:
|
|||
UninferredResetCastOp, ConstCastOp, RefCastOp,
|
||||
mlir::UnrealizedConversionCastOp,
|
||||
// Property expressions.
|
||||
StringConstantOp, FIntegerConstantOp, BoolConstantOp>(
|
||||
StringConstantOp, FIntegerConstantOp, BoolConstantOp, MapCreateOp>(
|
||||
[&](auto expr) -> ResultType {
|
||||
return thisCast->visitExpr(expr, args...);
|
||||
})
|
||||
|
@ -205,6 +205,7 @@ public:
|
|||
HANDLE(StringConstantOp, Unhandled);
|
||||
HANDLE(FIntegerConstantOp, Unhandled);
|
||||
HANDLE(BoolConstantOp, Unhandled);
|
||||
HANDLE(MapCreateOp, Unhandled);
|
||||
#undef HANDLE
|
||||
};
|
||||
|
||||
|
|
|
@ -3603,6 +3603,73 @@ LogicalResult ListCreateOp::verify() {
|
|||
return success();
|
||||
}
|
||||
|
||||
ParseResult MapCreateOp::parse(OpAsmParser &parser, OperationState &result) {
|
||||
llvm::SmallVector<OpAsmParser::UnresolvedOperand, 16> keys;
|
||||
llvm::SmallVector<OpAsmParser::UnresolvedOperand, 16> values;
|
||||
|
||||
MapType type;
|
||||
|
||||
auto parsePair = [&]() {
|
||||
OpAsmParser::UnresolvedOperand key, value;
|
||||
if (parser.parseOperand(key) || parser.parseArrow() ||
|
||||
parser.parseOperand(value))
|
||||
return failure();
|
||||
keys.push_back(key);
|
||||
values.push_back(value);
|
||||
return success();
|
||||
};
|
||||
if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::OptionalParen,
|
||||
parsePair) ||
|
||||
parser.parseOptionalAttrDict(result.attributes) ||
|
||||
parser.parseColonType(type))
|
||||
return failure();
|
||||
result.addTypes(type);
|
||||
|
||||
if (parser.resolveOperands(keys, type.getKeyType(), result.operands) ||
|
||||
parser.resolveOperands(values, type.getValueType(), result.operands))
|
||||
return failure();
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
void MapCreateOp::print(OpAsmPrinter &p) {
|
||||
p << " ";
|
||||
if (!getKeys().empty()) {
|
||||
p << "(";
|
||||
llvm::interleaveComma(llvm::zip_equal(getKeys(), getValues()), p,
|
||||
[&](auto pair) {
|
||||
auto &[key, value] = pair;
|
||||
p.printOperand(key);
|
||||
p << " -> ";
|
||||
p.printOperand(value);
|
||||
});
|
||||
p << ")";
|
||||
}
|
||||
p.printOptionalAttrDict((*this)->getAttrs());
|
||||
p << " : " << getType();
|
||||
}
|
||||
|
||||
LogicalResult MapCreateOp::verify() {
|
||||
if (getKeys().empty())
|
||||
return success();
|
||||
|
||||
if (!llvm::all_equal(getKeys().getTypes()))
|
||||
return emitOpError("has keys that are not all the same type");
|
||||
if (!llvm::all_equal(getValues().getTypes()))
|
||||
return emitOpError("has values that are not all the same type");
|
||||
|
||||
if (getKeys().front().getType() != getType().getKeyType())
|
||||
return emitOpError("has unexpected key type ")
|
||||
<< getKeys().front().getType() << " instead of map key type "
|
||||
<< getType().getKeyType();
|
||||
|
||||
if (getValues().front().getType() != getType().getValueType())
|
||||
return emitOpError("has unexpected value type ")
|
||||
<< getValues().front().getType() << " instead of map value type "
|
||||
<< getType().getValueType();
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult BundleCreateOp::verify() {
|
||||
BundleType resultType = getType();
|
||||
if (resultType.getNumElements() != getFields().size())
|
||||
|
|
|
@ -298,8 +298,11 @@ FIRToken FIRLexer::lexTokenImpl() {
|
|||
case '\'':
|
||||
return lexString(tokStart, /*isVerbatim=*/true);
|
||||
|
||||
case '+':
|
||||
case '-':
|
||||
if (*curPtr == '>')
|
||||
return ++curPtr, formToken(FIRToken::minus_greater, tokStart);
|
||||
return lexNumber(tokStart);
|
||||
case '+':
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
|
|
|
@ -257,7 +257,10 @@ struct FIRParser {
|
|||
const Twine &message);
|
||||
ParseResult parseEnumType(FIRRTLType &result);
|
||||
ParseResult parseListType(FIRRTLType &result);
|
||||
ParseResult parseMapType(FIRRTLType &result);
|
||||
ParseResult parseType(FIRRTLType &result, const Twine &message);
|
||||
// Parse a property type specifically.
|
||||
ParseResult parsePropertyType(PropertyType &result, const Twine &message);
|
||||
|
||||
ParseResult parseOptionalRUW(RUWAttr &result);
|
||||
|
||||
|
@ -795,25 +798,47 @@ ParseResult FIRParser::parseEnumType(FIRRTLType &result) {
|
|||
return success();
|
||||
}
|
||||
|
||||
ParseResult FIRParser::parsePropertyType(PropertyType &result,
|
||||
const Twine &message) {
|
||||
FIRRTLType type;
|
||||
if (parseType(type, message))
|
||||
return failure();
|
||||
auto prop = type_dyn_cast<PropertyType>(type);
|
||||
if (!prop)
|
||||
return emitError("expected property type");
|
||||
result = prop;
|
||||
return success();
|
||||
}
|
||||
|
||||
/// list-type ::= 'List' '<' type '>'
|
||||
ParseResult FIRParser::parseListType(FIRRTLType &result) {
|
||||
auto loc = getToken().getLoc();
|
||||
consumeToken(FIRToken::kw_List);
|
||||
|
||||
FIRRTLType type;
|
||||
PropertyType elementType;
|
||||
if (parseToken(FIRToken::less, "expected '<' in List type") ||
|
||||
parseType(type, "expected List element type") ||
|
||||
parsePropertyType(elementType, "expected List element type") ||
|
||||
parseToken(FIRToken::greater, "expected '>' in List type"))
|
||||
return failure();
|
||||
|
||||
auto elementType = type_dyn_cast<PropertyType>(type);
|
||||
if (!elementType)
|
||||
return emitError(loc, "expected property type");
|
||||
|
||||
result = ListType::get(getContext(), elementType);
|
||||
return success();
|
||||
}
|
||||
|
||||
/// map-type ::= 'Map' '<' type ',' type '>'
|
||||
ParseResult FIRParser::parseMapType(FIRRTLType &result) {
|
||||
consumeToken(FIRToken::kw_Map);
|
||||
|
||||
PropertyType key, value;
|
||||
if (parseToken(FIRToken::less, "expected '<' in Map type") ||
|
||||
parsePropertyType(key, "expected Map key type") ||
|
||||
parsePropertyType(value, "expected Map value type") ||
|
||||
parseToken(FIRToken::greater, "expected '>' in Map type"))
|
||||
return failure();
|
||||
|
||||
result = MapType::get(getContext(), key, value);
|
||||
return success();
|
||||
}
|
||||
|
||||
/// type ::= 'Clock'
|
||||
/// ::= 'Reset'
|
||||
/// ::= 'AsyncReset'
|
||||
|
@ -827,6 +852,7 @@ ParseResult FIRParser::parseListType(FIRRTLType &result) {
|
|||
/// ::= 'const' type
|
||||
/// ::= 'String'
|
||||
/// ::= list-type
|
||||
/// ::= map-type
|
||||
/// ::= id
|
||||
///
|
||||
/// field: 'flip'? fieldId ':' type
|
||||
|
@ -1040,6 +1066,10 @@ ParseResult FIRParser::parseType(FIRRTLType &result, const Twine &message) {
|
|||
if (requireFeature({3, 2, 0}, "Lists") || parseListType(result))
|
||||
return failure();
|
||||
break;
|
||||
case FIRToken::kw_Map:
|
||||
if (requireFeature({3, 2, 0}, "Maps") || parseMapType(result))
|
||||
return failure();
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle postfix vector sizes.
|
||||
|
@ -1544,6 +1574,7 @@ private:
|
|||
ParseResult parsePrimExp(Value &result);
|
||||
ParseResult parseIntegerLiteralExp(Value &result);
|
||||
ParseResult parseListExp(Value &result);
|
||||
ParseResult parseMapExp(Value &result);
|
||||
|
||||
std::optional<ParseResult> parseExpWithLeadingKeyword(FIRToken keyword);
|
||||
|
||||
|
@ -1847,6 +1878,15 @@ ParseResult FIRStmtParser::parseExpImpl(Value &result, const Twine &message,
|
|||
return failure();
|
||||
break;
|
||||
}
|
||||
case FIRToken::kw_Map: {
|
||||
if (requireFeature({3, 2, 0}, "Maps"))
|
||||
return failure();
|
||||
if (isLeadingStmt)
|
||||
return emitError("unexpected Map<>() as start of statement");
|
||||
if (parseMapExp(result))
|
||||
return failure();
|
||||
break;
|
||||
}
|
||||
case FIRToken::lp_path:
|
||||
if (isLeadingStmt)
|
||||
return emitError("unexpected path() as start of statement");
|
||||
|
@ -2295,6 +2335,50 @@ ParseResult FIRStmtParser::parseListExp(Value &result) {
|
|||
return success();
|
||||
}
|
||||
|
||||
/// kv-pair ::= exp '->' exp
|
||||
/// map-exp ::= map-type '(' ( kv-pair ( ',' kv-pair )* )? ')'
|
||||
ParseResult FIRStmtParser::parseMapExp(Value &result) {
|
||||
auto loc = getToken().getLoc();
|
||||
FIRRTLType type;
|
||||
if (parseMapType(type))
|
||||
return failure();
|
||||
auto mapType = type_cast<MapType>(type);
|
||||
auto keyType = mapType.getKeyType();
|
||||
auto valueType = mapType.getValueType();
|
||||
|
||||
if (parseToken(FIRToken::l_paren, "expected '(' in Map expression"))
|
||||
return failure();
|
||||
|
||||
SmallVector<Value, 3> keys, values;
|
||||
if (parseListUntil(FIRToken::r_paren, [&]() -> ParseResult {
|
||||
Value key, value;
|
||||
locationProcessor.setLoc(loc);
|
||||
if (parseExp(key, "expected key expression in Map expression") ||
|
||||
parseToken(FIRToken::minus_greater,
|
||||
"expected '->' in Map expression") ||
|
||||
parseExp(value, "expected value expression in Map expression"))
|
||||
return failure();
|
||||
|
||||
if (key.getType() != keyType)
|
||||
return emitError(loc, "unexpected expression of type ")
|
||||
<< key.getType() << " for key in Map expression, expected "
|
||||
<< keyType;
|
||||
if (value.getType() != valueType)
|
||||
return emitError(loc, "unexpected expression of type ")
|
||||
<< value.getType() << " for value in Map expression, expected "
|
||||
<< valueType;
|
||||
|
||||
keys.push_back(key);
|
||||
values.push_back(value);
|
||||
return success();
|
||||
}))
|
||||
return failure();
|
||||
|
||||
locationProcessor.setLoc(loc);
|
||||
result = builder.create<MapCreateOp>(mapType, keys, values);
|
||||
return success();
|
||||
}
|
||||
|
||||
/// The .fir grammar has the annoying property where:
|
||||
/// 1) some statements start with keywords
|
||||
/// 2) some start with an expression
|
||||
|
|
|
@ -75,6 +75,7 @@ TOK_PUNCTUATION(r_square, "]")
|
|||
TOK_PUNCTUATION(less, "<")
|
||||
TOK_PUNCTUATION(less_equal, "<=")
|
||||
TOK_PUNCTUATION(less_minus, "<-")
|
||||
TOK_PUNCTUATION(minus_greater, "->")
|
||||
TOK_PUNCTUATION(greater, ">")
|
||||
TOK_PUNCTUATION(equal, "=")
|
||||
TOK_PUNCTUATION(equal_greater, "=>")
|
||||
|
@ -92,6 +93,7 @@ TOK_KEYWORD(Fixed)
|
|||
TOK_KEYWORD(Inst)
|
||||
TOK_KEYWORD(Integer)
|
||||
TOK_KEYWORD(List)
|
||||
TOK_KEYWORD(Map)
|
||||
TOK_KEYWORD(Path)
|
||||
TOK_KEYWORD(Probe)
|
||||
TOK_KEYWORD(RWProbe)
|
||||
|
|
|
@ -1355,6 +1355,35 @@ firrtl.circuit "MixedList" {
|
|||
|
||||
// -----
|
||||
|
||||
// Reject map.create with mixed key types.
|
||||
firrtl.circuit "MixedMapKeys" {
|
||||
firrtl.module @MixedMapKeys(
|
||||
in %s: !firrtl.string,
|
||||
// expected-note @below {{prior use here}}
|
||||
in %int: !firrtl.integer
|
||||
) {
|
||||
// expected-error @below {{use of value '%int' expects different type than prior uses: '!firrtl.string' vs '!firrtl.integer'}}
|
||||
firrtl.map.create (%s -> %int, %int -> %int) : !firrtl.map<string, integer>
|
||||
}
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
// Reject map.create with mixed value types.
|
||||
firrtl.circuit "MixedMapValues" {
|
||||
firrtl.module @MixedMapValues(
|
||||
in %s1: !firrtl.string,
|
||||
// expected-note @below {{prior use here}}
|
||||
in %s2: !firrtl.string,
|
||||
in %int: !firrtl.integer
|
||||
) {
|
||||
// expected-error @below {{use of value '%s2' expects different type than prior uses: '!firrtl.integer' vs '!firrtl.string'}}
|
||||
firrtl.map.create (%s1 -> %int, %s2 -> %s2) : !firrtl.map<string, integer>
|
||||
}
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
// Reject list.create with elements of wrong type compared to result type.
|
||||
firrtl.circuit "ListCreateWrongType" {
|
||||
firrtl.module @ListCreateWrongType(
|
||||
|
@ -1368,6 +1397,19 @@ firrtl.circuit "ListCreateWrongType" {
|
|||
|
||||
// -----
|
||||
|
||||
// Reject map.create with consistent but wrong key/value elements.
|
||||
firrtl.circuit "MapCreateWrongType" {
|
||||
firrtl.module @MapCreateWrongType(
|
||||
// expected-note @below {{prior use here}}
|
||||
in %int: !firrtl.integer
|
||||
) {
|
||||
// expected-error @below {{use of value '%int' expects different type than prior uses: '!firrtl.string' vs '!firrtl.integer'}}
|
||||
firrtl.map.create (%int -> %int) : !firrtl.map<integer, string>
|
||||
}
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
// Reject list.create that doesn't create a list.
|
||||
firrtl.circuit "ListCreateNotList" {
|
||||
firrtl.module @ListCreateNotList() {
|
||||
|
|
|
@ -1776,3 +1776,40 @@ circuit BasicProps :
|
|||
; CHECK-NEXT: firrtl.propassign %nested, %[[NESTED]]
|
||||
propassign nested, List<List<String>>(List<String>(), List<String>(String("test")), List<String>())
|
||||
|
||||
; CHECK-LABEL: module private @Map
|
||||
module Map :
|
||||
; CHECK-SAME: in %list: !firrtl.list<string>
|
||||
input list : List<String>
|
||||
; CHECK-SAME: out %str2str: !firrtl.map<string, string>
|
||||
; CHECK-SAME: out %str2objs: !firrtl.map<string, class<@SimpleClass({{.*}})>>
|
||||
; CHECK-SAME: out %int2list: !firrtl.map<integer, list<string>>
|
||||
; CHECK-SAME: out %empty: !firrtl.map<integer, integer>
|
||||
output str2str : Map<String, String>
|
||||
output str2objs : Map<String, Inst<SimpleClass>>
|
||||
output int2list : Map<Integer, List<String>>
|
||||
output empty : Map<Integer, Integer>
|
||||
|
||||
; CHECK-NEXT: %[[HELLO:.+]] = firrtl.string "hello"
|
||||
; CHECK-NEXT: %[[WORLD:.+]] = firrtl.string "world"
|
||||
; CHECK-NEXT: %[[STRINGS:.+]] = firrtl.map.create (%[[HELLO]] -> %[[WORLD]]) : !firrtl.map<string, string>
|
||||
; CHECK-NEXT: firrtl.propassign %str2str, %[[STRINGS]] : !firrtl.map<string, string>
|
||||
propassign str2str, Map<String, String>(String("hello") -> String("world"))
|
||||
|
||||
; CHECK-NEXT: %[[OBJ:.+]] = firrtl.object
|
||||
; CHECK-NEXT: %[[HELLO:.+]] = firrtl.string "hello"
|
||||
; CHECK-NEXT: %[[WORLD:.+]] = firrtl.string "world"
|
||||
; CHECK-NEXT: %[[OBJS:.+]] = firrtl.map.create (%[[HELLO]] -> %[[OBJ]], %[[WORLD]] -> %[[OBJ]]) : !firrtl.map<string, class<@SimpleClass({{.*}})>>
|
||||
; CHECK-NEXT: firrtl.propassign %str2objs, %[[OBJS]] : !firrtl.map<string, class
|
||||
object obj of SimpleClass
|
||||
propassign str2objs, Map<String, Inst<SimpleClass>>(String("hello") -> obj, String("world") -> obj)
|
||||
|
||||
; CHECK-NEXT: %[[FIVE:.+]] = firrtl.integer 5
|
||||
; CHECK-NEXT: %[[EMPTY:.+]] = firrtl.list.create : !firrtl.list<string>
|
||||
; CHECK-NEXT: %[[TEN:.+]] = firrtl.integer 10
|
||||
; CHECK-NEXT: %[[INTLISTMAP:.+]] = firrtl.map.create (%[[FIVE]] -> %[[EMPTY]], %[[TEN]] -> %list) : !firrtl.map<integer, list<string>>
|
||||
; CHECK-NEXT: firrtl.propassign %int2list, %[[INTLISTMAP]]
|
||||
propassign int2list, Map<Integer, List<String>>(Integer(5) -> List<String>(), Integer(10) -> list)
|
||||
|
||||
; CHECK-NEXT: %[[EMPTY:.+]] = firrtl.map.create : !firrtl.map<integer, integer>
|
||||
; CHECK-NEXT: firrtl.propassign %empty, %[[EMPTY]]
|
||||
propassign empty, Map<Integer, Integer>()
|
||||
|
|
|
@ -1021,3 +1021,61 @@ circuit Top:
|
|||
; expected-error @below {{only hardware types can be 'const'}}
|
||||
output out : const Bool
|
||||
|
||||
;// -----
|
||||
; Maps not yet supported.
|
||||
FIRRTL version 3.1.0
|
||||
circuit MapStrToInt:
|
||||
module MapStrToInt:
|
||||
; expected-error @below {{Maps are a FIRRTL 3.2.0+ feature}}
|
||||
output a : Map<String, Integer>
|
||||
|
||||
;// -----
|
||||
; Map with non-property key type.
|
||||
FIRRTL version 3.2.0
|
||||
circuit MapUIntTo:
|
||||
module MapUIntTo:
|
||||
; expected-error @below {{expected property type}}
|
||||
output a : Map<UInt, String>
|
||||
|
||||
;// -----
|
||||
; Map with non-property value type.
|
||||
FIRRTL version 3.2.0
|
||||
circuit MapToUInt:
|
||||
module MapToUInt:
|
||||
; expected-error @below {{expected property type}}
|
||||
output a : Map<String, UInt>
|
||||
|
||||
;// -----
|
||||
; Wrong type of keys in Map expression.
|
||||
FIRRTL version 3.2.0
|
||||
circuit MapToString:
|
||||
module MapToString:
|
||||
output a : Map<Integer, String>
|
||||
; expected-error @below {{unexpected expression of type '!firrtl.string' for key in Map expression, expected '!firrtl.integer'}}
|
||||
propassign a, Map<Integer, String>(String("hello") -> String("world"))
|
||||
|
||||
;// -----
|
||||
; Wrong type of values in Map expression.
|
||||
FIRRTL version 3.2.0
|
||||
circuit MapBoolTo:
|
||||
module MapBoolTo:
|
||||
output a : Map<Bool, Integer>
|
||||
; expected-error @below {{unexpected expression of type '!firrtl.string' for value in Map expression, expected '!firrtl.integer'}}
|
||||
propassign a, Map<Bool, Integer>(Bool(true) -> String("world"))
|
||||
|
||||
|
||||
;// -----
|
||||
; Map should not be leading a statement.
|
||||
FIRRTL version 3.2.0
|
||||
circuit LeadingMap:
|
||||
module LeadingMap:
|
||||
; expected-error @below {{unexpected Map<>() as start of statement}}
|
||||
Map<String>()
|
||||
|
||||
;// -----
|
||||
; Map should not be const.
|
||||
FIRRTL version 3.2.0
|
||||
circuit ConstMap:
|
||||
module ConstMap:
|
||||
; expected-error @below {{only hardware types can be 'const'}}
|
||||
output m: const Map<String, String>
|
||||
|
|
|
@ -294,7 +294,18 @@ firrtl.module @ListTest(in %s1: !firrtl.string,
|
|||
// CHECK-LABEL: MapTest
|
||||
// CHECK-SAME: (in %in: !firrtl.map<integer, string>, out %out: !firrtl.map<integer, string>)
|
||||
firrtl.module @MapTest(in %in: !firrtl.map<integer, string>, out %out: !firrtl.map<integer, string>) {
|
||||
// CHECK-NEXT: propassign %out, %in : !firrtl.map<integer, string>
|
||||
firrtl.propassign %out, %in : !firrtl.map<integer, string>
|
||||
|
||||
// CHECK-NEXT: %[[EMPTY:.+]] = firrtl.map.create : !firrtl.map<integer, string>
|
||||
%empty = firrtl.map.create : !firrtl.map<integer, string>
|
||||
|
||||
// CHECK-NEXT: %[[HELLO:.+]] = firrtl.string "hello"
|
||||
// CHECK-NEXT: %[[WORLD:.+]] = firrtl.string "world"
|
||||
// CHECK-NEXT: %{{.+}} = firrtl.map.create (%in -> %[[HELLO]], %[[EMPTY]] -> %[[WORLD]]) : !firrtl.map<map<integer, string>, string>
|
||||
%hello = firrtl.string "hello"
|
||||
%world = firrtl.string "world"
|
||||
%mapmap = firrtl.map.create (%in -> %hello, %empty -> %world) : !firrtl.map<map<integer, string>, string>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: PropertyNestedTest
|
||||
|
|
Loading…
Reference in New Issue