From 7482d718075b908bc4bda242486e51c83cef7274 Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Wed, 27 Mar 2024 14:59:32 -0500 Subject: [PATCH 1/3] [FIRRTL] Generic intrinsic parsing/emitter support. --- lib/Dialect/FIRRTL/Export/FIREmitter.cpp | 111 ++++++++--- lib/Dialect/FIRRTL/Import/FIRParser.cpp | 232 ++++++++++++++++------- test/Dialect/FIRRTL/emit-basic.mlir | 48 +++++ test/Dialect/FIRRTL/parse-basic.fir | 33 ++++ test/Dialect/FIRRTL/parse-errors.fir | 35 ++++ 5 files changed, 365 insertions(+), 94 deletions(-) diff --git a/lib/Dialect/FIRRTL/Export/FIREmitter.cpp b/lib/Dialect/FIRRTL/Export/FIREmitter.cpp index 75ffaff4ce7c..de42fccb9c98 100644 --- a/lib/Dialect/FIRRTL/Export/FIREmitter.cpp +++ b/lib/Dialect/FIRRTL/Export/FIREmitter.cpp @@ -62,6 +62,10 @@ struct Emitter { void emitDeclaration(OptionOp op); void emitEnabledLayers(ArrayRef layers); + void emitParamAssign(ParamDeclAttr param, Operation *op, + std::optional wordBeforeLHS = std::nullopt); + void emitGenericIntrinsic(GenericIntrinsicOp op); + // Statement emission void emitStatementsInBlock(Block &block); void emitStatement(WhenOp op); @@ -91,6 +95,7 @@ struct Emitter { void emitStatement(RefReleaseOp op); void emitStatement(RefReleaseInitialOp op); void emitStatement(LayerBlockOp op); + void emitStatement(GenericIntrinsicOp op); template void emitVerifStatement(T op, StringRef mnemonic); @@ -120,6 +125,7 @@ struct Emitter { void emitExpression(DoubleConstantOp op); void emitExpression(ListCreateOp op); void emitExpression(UnresolvedPathOp op); + void emitExpression(GenericIntrinsicOp op); void emitPrimExpr(StringRef mnemonic, Operation *op, ArrayRef attrs = {}); @@ -419,6 +425,57 @@ void Emitter::emitEnabledLayers(ArrayRef layers) { } } +void Emitter::emitParamAssign(ParamDeclAttr param, Operation *op, + std::optional wordBeforeLHS) { + if (wordBeforeLHS) { + ps << *wordBeforeLHS << PP::nbsp; + } + ps << PPExtString(param.getName().strref()) << PP::nbsp << "=" << PP::nbsp; + TypeSwitch(param.getValue()) + .Case([&](auto attr) { ps.addAsString(attr.getValue()); }) + .Case([&](auto attr) { + SmallString<16> str; + attr.getValue().toString(str); + ps << str; + }) + .Case( + [&](auto attr) { ps.writeQuotedEscaped(attr.getValue()); }) + .Default([&](auto attr) { + emitOpError(op, "with unsupported parameter attribute: ") << attr; + ps << ""; + }); +} + +void Emitter::emitGenericIntrinsic(GenericIntrinsicOp op) { + ps << "intrinsic("; + ps.scopedBox(PP::cbox0, [&]() { + ps.scopedBox(PP::ibox2, [&]() { + ps << op.getIntrinsic(); + ps.scopedBox(PP::ibox0, [&]() { + auto params = op.getParameters(); + if (!params.empty()) { + ps << "<"; + ps.scopedBox(PP::ibox0, [&]() { + interleaveComma( + params.getAsRange(), + [&](ParamDeclAttr param) { emitParamAssign(param, op); }); + }); + ps << ">"; + } + }); + if (op.getNumResults() != 0) + emitTypeWithColon(op.getResult().getType()); + }); + if (op.getNumOperands() != 0) { + ps << "," << PP::space; + ps.scopedBox(PP::ibox0, [&]() { interleaveComma(op->getOperands()); }); + } + ps << ")"; + }); +} + /// Emit an entire module. void Emitter::emitModule(FModuleOp op) { startStatement(); @@ -519,27 +576,9 @@ void Emitter::emitModulePorts(ArrayRef ports, } void Emitter::emitModuleParameters(Operation *op, ArrayAttr parameters) { - for (auto param : llvm::map_range(parameters, [](Attribute attr) { - return cast(attr); - })) { + for (auto param : parameters.getAsRange()) { startStatement(); - // TODO: AssignLike ? - ps << "parameter " << PPExtString(param.getName().getValue()) << " = "; - TypeSwitch(param.getValue()) - .Case([&](auto attr) { ps.addAsString(attr.getValue()); }) - .Case([&](auto attr) { - SmallString<16> str; - attr.getValue().toString(str); - ps << str; - }) - .Case( - [&](auto attr) { ps.writeQuotedEscaped(attr.getValue()); }) - .Default([&](auto attr) { - emitOpError(op, "with unsupported parameter attribute: ") << attr; - ps << ""; - }); + emitParamAssign(param, op, PPExtString("parameter")); setPendingNewline(); } } @@ -580,7 +619,14 @@ void Emitter::emitDeclaration(OptionOp op) { /// Check if an operation is inlined into the emission of their users. For /// example, subfields are always inlined. static bool isEmittedInline(Operation *op) { - return isExpression(op) && !isa(op); + // FIRRTL expressions are statically classified as always inlineable. + // InvalidValueOp never is inlined, and is handled specially. + // GenericIntrinsicOp is inlined if has exactly one use (only emit once) + // that is not emitted inline. This is to ensure it is emitted inline + // in common cases, but only inspect one level deep. + return (isExpression(op) && !isa(op)) || + (isa(op) && op->hasOneUse() && + !isEmittedInline(*op->getUsers().begin())); } void Emitter::emitStatementsInBlock(Block &block) { @@ -596,7 +642,8 @@ void Emitter::emitStatementsInBlock(Block &block) { InvalidValueOp, SeqMemOp, CombMemOp, MemoryPortOp, MemoryDebugPortOp, MemoryPortAccessOp, RefDefineOp, RefForceOp, RefForceInitialOp, RefReleaseOp, RefReleaseInitialOp, - LayerBlockOp>([&](auto op) { emitStatement(op); }) + LayerBlockOp, GenericIntrinsicOp>( + [&](auto op) { emitStatement(op); }) .Default([&](auto op) { startStatement(); ps << "// operation " << PPExtString(op->getName().getStringRef()); @@ -1087,6 +1134,20 @@ void Emitter::emitStatement(InvalidValueOp op) { emitLocationAndNewLine(op); } +void Emitter::emitStatement(GenericIntrinsicOp op) { + startStatement(); + if (op.use_empty()) + emitGenericIntrinsic(op); + else { + assert(!isEmittedInline(op)); + auto name = circuitNamespace.newName("_gen_int"); + addValueName(op.getResult(), name); + emitAssignLike([&]() { ps << "node " << PPExtString(name); }, + [&]() { emitGenericIntrinsic(op); }); + } + emitLocationAndNewLine(op); +} + void Emitter::emitExpression(Value value) { // Handle the trivial case where we already have a name for this value which // we can use. @@ -1114,7 +1175,7 @@ void Emitter::emitExpression(Value value) { BitsPrimOp, HeadPrimOp, TailPrimOp, PadPrimOp, MuxPrimOp, ShlPrimOp, ShrPrimOp, UninferredResetCastOp, ConstCastOp, StringConstantOp, FIntegerConstantOp, BoolConstantOp, DoubleConstantOp, ListCreateOp, - UnresolvedPathOp, + UnresolvedPathOp, GenericIntrinsicOp, // Reference expressions RefSendOp, RefResolveOp, RefSubOp, RWProbeOp, RefCastOp>( [&](auto op) { @@ -1302,6 +1363,10 @@ void Emitter::emitExpression(UnresolvedPathOp op) { ps << ")"; } +void Emitter::emitExpression(GenericIntrinsicOp op) { + emitGenericIntrinsic(op); +} + void Emitter::emitExpression(ConstCastOp op) { emitExpression(op.getInput()); } void Emitter::emitPrimExpr(StringRef mnemonic, Operation *op, diff --git a/lib/Dialect/FIRRTL/Import/FIRParser.cpp b/lib/Dialect/FIRRTL/Import/FIRParser.cpp index efd98ea5fd18..5310dae94580 100644 --- a/lib/Dialect/FIRRTL/Import/FIRParser.cpp +++ b/lib/Dialect/FIRRTL/Import/FIRParser.cpp @@ -282,6 +282,9 @@ struct FIRParser { ParseResult parseOptionalRUW(RUWAttr &result); + ParseResult parseParameter(StringAttr &resultName, TypedAttr &resultValue, + SMLoc &resultLoc); + /// The version of FIRRTL to use for this parser. FIRVersion version; @@ -1150,6 +1153,72 @@ ParseResult FIRParser::parseOptionalRUW(RUWAttr &result) { return success(); } +/// param ::= id '=' intLit +/// ::= id '=' StringLit +/// ::= id '=' floatingpoint +/// ::= id '=' VerbatimStringLit +ParseResult FIRParser::parseParameter(StringAttr &resultName, + TypedAttr &resultValue, + SMLoc &resultLoc) { + mlir::Builder builder(getContext()); + + auto loc = getToken().getLoc(); + + StringRef name; + if (parseId(name, "expected parameter name") || + parseToken(FIRToken::equal, "expected '=' in parameter")) + return failure(); + + TypedAttr value; + switch (getToken().getKind()) { + default: + return emitError("expected parameter value"), failure(); + case FIRToken::integer: + case FIRToken::signed_integer: { + APInt result; + if (parseIntLit(result, "invalid integer parameter")) + return failure(); + + // If the integer parameter is less than 32-bits, sign extend this to a + // 32-bit value. This needs to eventually emit as a 32-bit value in + // Verilog and we want to get the size correct immediately. + if (result.getBitWidth() < 32) + result = result.sext(32); + + value = builder.getIntegerAttr( + builder.getIntegerType(result.getBitWidth(), result.isSignBitSet()), + result); + break; + } + case FIRToken::string: { + // Drop the double quotes and unescape. + value = builder.getStringAttr(getToken().getStringValue()); + consumeToken(FIRToken::string); + break; + } + case FIRToken::verbatim_string: { + // Drop the single quotes and unescape the ones inside. + auto text = builder.getStringAttr(getToken().getVerbatimStringValue()); + value = hw::ParamVerbatimAttr::get(text); + consumeToken(FIRToken::verbatim_string); + break; + } + case FIRToken::floatingpoint: + double v; + if (!llvm::to_float(getTokenSpelling(), v)) + return emitError("invalid float parameter syntax"), failure(); + + value = builder.getF64FloatAttr(v); + consumeToken(FIRToken::floatingpoint); + break; + } + + resultName = builder.getStringAttr(name); + resultValue = value; + resultLoc = loc; + return success(); +} + //===----------------------------------------------------------------------===// // FIRModuleContext //===----------------------------------------------------------------------===// @@ -1595,6 +1664,17 @@ struct FIRStmtParser : public FIRParser { ParseResult parseRWProbeStaticRefExp(FieldRef &refResult, Type &type, const Twine &message); + // Generic intrinsic parsing. + ParseResult parseIntrinsic(Value &result, bool isStatement); + ParseResult parseIntrinsicStmt() { + Value unused; + return parseIntrinsic(unused, /*isStatement=*/true); + } + ParseResult parseIntrinsicExp(Value &result) { + return parseIntrinsic(result, /*isStatement=*/false); + } + ParseResult parseOptionalParams(ArrayAttr &resultParameters); + template FailureOr emitCachedSubAccess(Value base, ArrayRef attrs, @@ -1943,6 +2023,12 @@ ParseResult FIRStmtParser::parseExpImpl(Value &result, const Twine &message, return failure(); break; + case FIRToken::kw_intrinsic: + if (requireFeature({4, 0, 0}, "generic intrinsics") || + parseIntrinsicExp(result)) + return failure(); + break; + // Otherwise there are a bunch of keywords that are treated as identifiers // try them. case FIRToken::identifier: // exp ::= id @@ -2583,7 +2669,10 @@ ParseResult FIRStmtParser::parseSimpleStmtImpl(unsigned stmtIndent) { if (requireFeature({4, 0, 0}, "layers")) return failure(); return parseLayerBlockOrGroup(stmtIndent); - + case FIRToken::kw_intrinsic: + if (requireFeature({4, 0, 0}, "generic intrinsics")) + return failure(); + return parseIntrinsicStmt(); default: { // Statement productions that start with an expression. Value lhs; @@ -3307,6 +3396,75 @@ ParseResult FIRStmtParser::parseRWProbeStaticRefExp(FieldRef &refResult, } } +/// intrinsic_expr ::= 'intrinsic(' Id (params)? ':' type exp* ')' +/// intrinsic_stmt ::= 'intrinsic(' Id (params)? (':' type )? exp* ')' +ParseResult FIRStmtParser::parseIntrinsic(Value &result, bool isStatement) { + auto startTok = consumeToken(FIRToken::kw_intrinsic); + StringRef intrinsic; + ArrayAttr parameters; + FIRRTLType type; + + if (parseToken(FIRToken::l_paren, "expected '(' in intrinsic expression") || + parseId(intrinsic, "expected intrinsic identifier") || + parseOptionalParams(parameters)) + return failure(); + + if (consumeIf(FIRToken::colon)) { + if (parseType(type, "expected intrinsic return type")) + return failure(); + } else if (!isStatement) + return emitError("expected ':' in intrinsic expression"); + + SmallVector operands; + auto loc = startTok.getLoc(); + if (parseListUntil(FIRToken::r_paren, [&]() -> ParseResult { + Value operand; + if (parseExp(operand, "expected operand in intrinsic")) + return failure(); + operands.push_back(operand); + locationProcessor.setLoc(loc); + return success(); + })) + return failure(); + + if (isStatement) + if (parseOptionalInfo()) + return failure(); + + locationProcessor.setLoc(loc); + + auto op = builder.create( + type, builder.getStringAttr(intrinsic), operands, parameters); + if (type) + result = op.getResult(); + return success(); +} + +/// params ::= '<' param param* '>' +ParseResult FIRStmtParser::parseOptionalParams(ArrayAttr &resultParameters) { + if (!consumeIf(FIRToken::less)) + return success(); + + SmallVector parameters; + SmallPtrSet seen; + if (parseListUntil(FIRToken::greater, [&]() -> ParseResult { + StringAttr name; + TypedAttr value; + SMLoc loc; + if (parseParameter(name, value, loc)) + return failure(); + if (!seen.insert(name).second) + return emitError(loc, "redefinition of parameter '" + + name.getValue() + "'"); + parameters.push_back(ParamDeclAttr::get(name, value)); + return success(); + })) + return failure(); + + resultParameters = ArrayAttr::get(getContext(), parameters); + return success(); +} + /// path ::= 'path(' StringLit ')' // NOLINTNEXTLINE(misc-no-recursion) ParseResult FIRStmtParser::parsePathExp(Value &result) { @@ -4466,8 +4624,6 @@ struct FIRCircuitParser : public FIRParser { SmallVectorImpl &resultPortLocs, unsigned indent); ParseResult parseParameterList(ArrayAttr &resultParameters); - ParseResult parseParameter(StringAttr &resultName, TypedAttr &resultValue, - SMLoc &resultLoc); ParseResult parseRefList(ArrayRef portList, ArrayAttr &internalPathsResult); @@ -4784,78 +4940,12 @@ ParseResult FIRCircuitParser::skipToModuleEnd(unsigned indent) { } } -/// parameter ::= 'parameter' id '=' intLit NEWLINE -/// parameter ::= 'parameter' id '=' StringLit NEWLINE -/// parameter ::= 'parameter' id '=' floatingpoint NEWLINE -/// parameter ::= 'parameter' id '=' VerbatimStringLit NEWLINE -ParseResult FIRCircuitParser::parseParameter(StringAttr &resultName, - TypedAttr &resultValue, - SMLoc &resultLoc) { - mlir::Builder builder(getContext()); - - consumeToken(FIRToken::kw_parameter); - auto loc = getToken().getLoc(); - - StringRef name; - if (parseId(name, "expected parameter name") || - parseToken(FIRToken::equal, "expected '=' in parameter")) - return failure(); - - TypedAttr value; - switch (getToken().getKind()) { - default: - return emitError("expected parameter value"), failure(); - case FIRToken::integer: - case FIRToken::signed_integer: { - APInt result; - if (parseIntLit(result, "invalid integer parameter")) - return failure(); - - // If the integer parameter is less than 32-bits, sign extend this to a - // 32-bit value. This needs to eventually emit as a 32-bit value in - // Verilog and we want to get the size correct immediately. - if (result.getBitWidth() < 32) - result = result.sext(32); - - value = builder.getIntegerAttr( - builder.getIntegerType(result.getBitWidth(), result.isSignBitSet()), - result); - break; - } - case FIRToken::string: { - // Drop the double quotes and unescape. - value = builder.getStringAttr(getToken().getStringValue()); - consumeToken(FIRToken::string); - break; - } - case FIRToken::verbatim_string: { - // Drop the single quotes and unescape the ones inside. - auto text = builder.getStringAttr(getToken().getVerbatimStringValue()); - value = hw::ParamVerbatimAttr::get(text); - consumeToken(FIRToken::verbatim_string); - break; - } - case FIRToken::floatingpoint: - double v; - if (!llvm::to_float(getTokenSpelling(), v)) - return emitError("invalid float parameter syntax"), failure(); - - value = builder.getF64FloatAttr(v); - consumeToken(FIRToken::floatingpoint); - break; - } - - resultName = builder.getStringAttr(name); - resultValue = value; - resultLoc = loc; - return success(); -} - /// parameter-list ::= parameter* +/// parameter ::= 'parameter' param NEWLINE ParseResult FIRCircuitParser::parseParameterList(ArrayAttr &resultParameters) { SmallVector parameters; SmallPtrSet seen; - while (getToken().is(FIRToken::kw_parameter)) { + while (consumeIf(FIRToken::kw_parameter)) { StringAttr name; TypedAttr value; SMLoc loc; diff --git a/test/Dialect/FIRRTL/emit-basic.mlir b/test/Dialect/FIRRTL/emit-basic.mlir index 06e45c3def1e..be86731a15f8 100644 --- a/test/Dialect/FIRRTL/emit-basic.mlir +++ b/test/Dialect/FIRRTL/emit-basic.mlir @@ -846,4 +846,52 @@ firrtl.circuit "Foo" { firrtl.instance_choice inst @DefaultTarget alternatives @Platform { @FPGA -> @FPGATarget, @ASIC -> @ASICTarget } () } + + // CHECK-LABEL: public module GenericIntrinsic : + firrtl.module public @GenericIntrinsic(in %clk: !firrtl.clock, + in %c : !firrtl.uint<1>, + out %s: !firrtl.uint<32>, + out %io1: !firrtl.uint<1>, + out %io2: !firrtl.uint<1>, + out %io3: !firrtl.uint<1>, + out %io4: !firrtl.uint<5>) { + // Common case should be emitted inline. + // CHECK: connect s, intrinsic(circt_sizeof : UInt<32>, clk) + %0 = firrtl.int.generic "circt_sizeof" %clk : (!firrtl.clock) -> !firrtl.uint<32> + firrtl.strictconnect %s, %0 : !firrtl.uint<32> + // CHECK-NEXT: connect io1, intrinsic(circt_isX : UInt<1>, clk) + %1 = firrtl.int.generic "circt_isX" %clk : (!firrtl.clock) -> !firrtl.uint<1> + firrtl.strictconnect %io1, %1 : !firrtl.uint<1> + // CHECK-NEXT: connect io2, intrinsic(circt_plusargs_test : UInt<1>) + %2 = firrtl.int.generic "circt_plusargs_test" : () -> !firrtl.uint<1> + firrtl.strictconnect %io2, %2 : !firrtl.uint<1> + + // CHECK-NOT: = + // CHECK-NEXT: intrinsic(circt_clock_gate : Clock, clk, c) + firrtl.int.generic "circt_clock_gate" %clk, %c : (!firrtl.clock, !firrtl.uint<1>) -> !firrtl.clock + + // Materialize node as needed when used multiple times. + // CHECK-NEXT: node [[PAV:.+]] + // CHECK-NEXT: = intrinsic(circt_plusargs_value + // CHECK-NEXT: : { found : UInt<1>, result : UInt<5> } + %3 = firrtl.int.generic "circt_plusargs_value" : () -> !firrtl.bundle, result: uint<5>> + // CHECK-NEXT: connect io3, [[PAV]].found + // CHECK-NEXT: connect io4, [[PAV]].result + %4 = firrtl.subfield %3[found] : !firrtl.bundle, result: uint<5>> + %5 = firrtl.subfield %3[result] : !firrtl.bundle, result: uint<5>> + firrtl.strictconnect %io3, %4 : !firrtl.uint<1> + firrtl.strictconnect %io4, %5 : !firrtl.uint<5> + + // Nested once should be inlined. + // CHECK-NEXT: intrinsic(circt_verif_assert, intrinsic(circt_isX : UInt<1>, c)) + %6 = firrtl.int.generic "circt_isX" %c : (!firrtl.uint<1>) -> !firrtl.uint<1> + firrtl.int.generic "circt_verif_assert" %6 : (!firrtl.uint<1>) -> () + + // Spill if nested > 1 for simplicity. + // CHECK-NEXT: node [[ISX:.+]] = intrinsic(circt_isX : UInt<1>, c) + // CHECK-NEXT: intrinsic(circt_verif_assert, intrinsic(circt_isX : UInt<1>, [[ISX]])) + %7 = firrtl.int.generic "circt_isX" %c : (!firrtl.uint<1>) -> !firrtl.uint<1> + %8 = firrtl.int.generic "circt_isX" %7 : (!firrtl.uint<1>) -> !firrtl.uint<1> + firrtl.int.generic "circt_verif_assert" %8 : (!firrtl.uint<1>) -> () + } } diff --git a/test/Dialect/FIRRTL/parse-basic.fir b/test/Dialect/FIRRTL/parse-basic.fir index 45123fc3f26c..5fe4bbfe9e9b 100644 --- a/test/Dialect/FIRRTL/parse-basic.fir +++ b/test/Dialect/FIRRTL/parse-basic.fir @@ -2080,3 +2080,36 @@ circuit StaticShiftRight: ; CHECK: %11 = firrtl.shr %x, 10 ; CHECK: %x_1 = firrtl.node {{.*}} %11 : !firrtl.sint node x_1 = shr(x, 10) + +;// ----- +FIRRTL version 4.0.0 +; CHECK-LABEL: circuit "GenericIntrinsics" +circuit GenericIntrinsics: + ; CHECK: firrtl.module @GenericIntrinsics + public module GenericIntrinsics: + input clock : Clock + input data : UInt<32> + input c : UInt<1> + + ; Statements + ; CHECK-NEXT: firrtl.int.generic "circt_verif_assert" %c : (!firrtl.uint<1>) -> () + intrinsic(circt_verif_assert, c) + ; CHECK-NEXT: firrtl.int.generic "circt_fpga_probe" %data, %clock : (!firrtl.uint<32>, !firrtl.clock) -> () + intrinsic(circt_fpga_probe, data, clock) + + ; Expressions + ; CHECK-NEXT: %[[PAV:.+]] = firrtl.int.generic "circt_plusargs_value" : () -> !firrtl.bundle, result: uint<5>> + ; CHECK-NEXT: %n = firrtl.node interesting_name %[[PAV]] + node n = intrinsic(circt_plusargs_value : { found : UInt<1>, result : UInt<5> }) + ; CHECK-NEXT: %[[PAT:.+]] = firrtl.int.generic "circt_plusargs_test" : () -> !firrtl.uint<3> + ; CHECK-NEXT: %n2 = firrtl.node interesting_name %[[PAT]] + node n2 = intrinsic(circt_plusargs_test : UInt<3>) + + + ; Statement with unused return value. + ; CHECK-NEXT: firrtl.int.generic "circt_clock_gate" %clock, %c : (!firrtl.clock, !firrtl.uint<1>) -> !firrtl.clock + intrinsic(circt_clock_gate : Clock, clock, c) + + ; CHECK-NEXT: %[[SZ:.+]] = firrtl.int.generic "circt_isX" + ; CHECK-NEXT: "circt_verif_assert" %[[SZ]] + intrinsic(circt_verif_assert, intrinsic(circt_isX: UInt<1>, data)) diff --git a/test/Dialect/FIRRTL/parse-errors.fir b/test/Dialect/FIRRTL/parse-errors.fir index f8f9e37d60a2..b2f1d7a1368a 100644 --- a/test/Dialect/FIRRTL/parse-errors.fir +++ b/test/Dialect/FIRRTL/parse-errors.fir @@ -1395,3 +1395,38 @@ circuit IntModuleBadIntrinsic: ; expected-error @below {{expected intrinsic name}} intrinsic = 0 module IntModule: + +;// ----- +FIRRTL version 4.0.0 +circuit GenericIntExprNoRet: + public module GenericIntExprNoRet: + ; expected-error @below {{expected ':' in intrinsic expression}} + node n = intrinsic(dummy) + +;// ----- +FIRRTL version 4.0.0 +circuit GenericIntNotPassive: + public module GenericIntNotPassive: + ; expected-error @below {{must be a passive base type}} + intrinsic(dummy : { flip x : UInt<1> }) + +;// ----- +FIRRTL version 4.0.0 +circuit GenericIntBadId: + public module GenericIntBadId: + ; expected-error @below {{expected intrinsic identifier}} + intrinsic(5) + +;// ----- +FIRRTL version 4.0.0 +circuit GenericIntUntermStr: + public module GenericIntUntermStr: + ; expected-error @below {{unterminated string}} + intrinsic) From 9a8412aa9415af5b1b49b449dbdf0b01683b535a Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Fri, 5 Apr 2024 09:00:14 -0500 Subject: [PATCH 2/3] [FIRRTL][Import] Add and use "intrinsic" left-paren keyword. Allows use of "intrinsic" as an identifier when not followed by the left-paren, e.g. `connect intrinsic.i, i`. --- lib/Dialect/FIRRTL/Import/FIRParser.cpp | 9 ++++----- lib/Dialect/FIRRTL/Import/FIRTokenKinds.def | 2 ++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/Dialect/FIRRTL/Import/FIRParser.cpp b/lib/Dialect/FIRRTL/Import/FIRParser.cpp index 5310dae94580..cc670023c1e0 100644 --- a/lib/Dialect/FIRRTL/Import/FIRParser.cpp +++ b/lib/Dialect/FIRRTL/Import/FIRParser.cpp @@ -2023,7 +2023,7 @@ ParseResult FIRStmtParser::parseExpImpl(Value &result, const Twine &message, return failure(); break; - case FIRToken::kw_intrinsic: + case FIRToken::lp_intrinsic: if (requireFeature({4, 0, 0}, "generic intrinsics") || parseIntrinsicExp(result)) return failure(); @@ -2669,7 +2669,7 @@ ParseResult FIRStmtParser::parseSimpleStmtImpl(unsigned stmtIndent) { if (requireFeature({4, 0, 0}, "layers")) return failure(); return parseLayerBlockOrGroup(stmtIndent); - case FIRToken::kw_intrinsic: + case FIRToken::lp_intrinsic: if (requireFeature({4, 0, 0}, "generic intrinsics")) return failure(); return parseIntrinsicStmt(); @@ -3399,13 +3399,12 @@ ParseResult FIRStmtParser::parseRWProbeStaticRefExp(FieldRef &refResult, /// intrinsic_expr ::= 'intrinsic(' Id (params)? ':' type exp* ')' /// intrinsic_stmt ::= 'intrinsic(' Id (params)? (':' type )? exp* ')' ParseResult FIRStmtParser::parseIntrinsic(Value &result, bool isStatement) { - auto startTok = consumeToken(FIRToken::kw_intrinsic); + auto startTok = consumeToken(FIRToken::lp_intrinsic); StringRef intrinsic; ArrayAttr parameters; FIRRTLType type; - if (parseToken(FIRToken::l_paren, "expected '(' in intrinsic expression") || - parseId(intrinsic, "expected intrinsic identifier") || + if (parseId(intrinsic, "expected intrinsic identifier") || parseOptionalParams(parameters)) return failure(); diff --git a/lib/Dialect/FIRRTL/Import/FIRTokenKinds.def b/lib/Dialect/FIRRTL/Import/FIRTokenKinds.def index e927e8bf76a1..50262e6ae6e2 100644 --- a/lib/Dialect/FIRRTL/Import/FIRTokenKinds.def +++ b/lib/Dialect/FIRRTL/Import/FIRTokenKinds.def @@ -179,6 +179,8 @@ TOK_LPKEYWORD(read) TOK_LPKEYWORD(probe) TOK_LPKEYWORD(rwprobe) +TOK_LPKEYWORD(intrinsic) + // These are for LPKEYWORD cases that correspond to a primitive operation. TOK_LPKEYWORD_PRIM(add, AddPrimOp, 2) TOK_LPKEYWORD_PRIM(and, AndPrimOp, 2) From 8cba9fce1c719325444c89a955b8c4de4d35cc20 Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Mon, 8 Apr 2024 10:34:50 -0500 Subject: [PATCH 3/3] [FIRRTL][FIRParser] Fixup optional param grammar comment. Co-authored-by: Robert Young --- lib/Dialect/FIRRTL/Import/FIRParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Dialect/FIRRTL/Import/FIRParser.cpp b/lib/Dialect/FIRRTL/Import/FIRParser.cpp index cc670023c1e0..1986fbc4d104 100644 --- a/lib/Dialect/FIRRTL/Import/FIRParser.cpp +++ b/lib/Dialect/FIRRTL/Import/FIRParser.cpp @@ -3439,7 +3439,7 @@ ParseResult FIRStmtParser::parseIntrinsic(Value &result, bool isStatement) { return success(); } -/// params ::= '<' param param* '>' +/// params ::= '<' param* '>' ParseResult FIRStmtParser::parseOptionalParams(ArrayAttr &resultParameters) { if (!consumeIf(FIRToken::less)) return success();