[ExportVerilog] Pull parameter expr printing into ModuleEmitter, NFC.

This gives parameter expresion printing access to the current module,
as well as the GlobalNameTable.
This commit is contained in:
Chris Lattner 2021-10-08 13:42:22 -07:00
parent 7df98af7c7
commit 061a0f300f
1 changed files with 170 additions and 161 deletions

View File

@ -94,163 +94,6 @@ struct SubExprInfo {
// Helper routines
//===----------------------------------------------------------------------===//
/// Helper that prints a parameter constant value in a Verilog compatible way.
/// This returns the precedence of the generated string.
static SubExprInfo
printParamValue(Attribute value, raw_ostream &os,
VerilogPrecedence parenthesizeIfLooserThan,
function_ref<InFlightDiagnostic()> emitError) {
if (auto intAttr = value.dyn_cast<IntegerAttr>()) {
IntegerType intTy = intAttr.getType().cast<IntegerType>();
APInt value = intAttr.getValue();
// We omit the width specifier if the value is <= 32-bits in size, which
// makes this more compatible with unknown width extmodules.
if (intTy.getWidth() > 32) {
// Sign comes out before any width specifier.
if (intTy.isSigned() && value.isNegative()) {
os << '-';
value = -value;
}
if (intTy.isSigned())
os << intTy.getWidth() << "'sd";
else
os << intTy.getWidth() << "'d";
}
value.print(os, intTy.isSigned());
return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
}
if (auto strAttr = value.dyn_cast<StringAttr>()) {
os << '"';
os.write_escaped(strAttr.getValue());
os << '"';
return {Symbol, IsUnsigned};
}
if (auto fpAttr = value.dyn_cast<FloatAttr>()) {
// TODO: relying on float printing to be precise is not a good idea.
os << fpAttr.getValueAsDouble();
return {Symbol, IsUnsigned};
}
if (auto verbatimParam = value.dyn_cast<ParamVerbatimAttr>()) {
os << verbatimParam.getValue().getValue();
return {Symbol, IsUnsigned};
}
if (auto parameterRef = value.dyn_cast<ParamDeclRefAttr>()) {
os << parameterRef.getName().getValue();
// TODO: Should we support signed parameters?
return {Symbol, IsUnsigned};
}
// Handle nested expressions.
auto expr = value.dyn_cast<ParamExprAttr>();
if (!expr) {
os << "<<UNKNOWN MLIRATTR: " << value << ">>";
emitError() << " = " << value;
return {LowestPrecedence, IsUnsigned};
}
StringRef operatorStr;
VerilogPrecedence subprecedence = ForceEmitMultiUse;
Optional<SubExprSignResult> operandSign;
switch (expr.getOpcode()) {
case PEO::Add:
operatorStr = " + ";
subprecedence = Addition;
break;
case PEO::Mul:
operatorStr = " * ";
subprecedence = Multiply;
break;
case PEO::And:
operatorStr = " & ";
subprecedence = And;
break;
case PEO::Or:
operatorStr = " | ";
subprecedence = Or;
break;
case PEO::Xor:
operatorStr = " ^ ";
subprecedence = Xor;
break;
case PEO::Shl:
operatorStr = " << ";
subprecedence = Shift;
break;
case PEO::ShrU:
// >> in verilog is always a logical shift even if operands are signed.
operatorStr = " >> ";
subprecedence = Shift;
break;
case PEO::ShrS:
// >>> in verilog is an arithmetic shift if both operands are signed.
operatorStr = " >>> ";
subprecedence = Shift;
operandSign = IsSigned;
break;
case PEO::DivU:
operatorStr = " / ";
subprecedence = Multiply;
operandSign = IsUnsigned;
break;
case PEO::DivS:
operatorStr = " / ";
subprecedence = Multiply;
operandSign = IsSigned;
break;
case PEO::ModU:
operatorStr = " % ";
subprecedence = Multiply;
operandSign = IsUnsigned;
break;
case PEO::ModS:
operatorStr = " % ";
subprecedence = Multiply;
operandSign = IsSigned;
break;
}
// Emit the specified operand with a $signed() or $unsigned() wrapper around
// it if context requires a specific signedness to compute the right value.
// This returns true if the operand is signed.
// TODO: This could try harder to omit redundant casts like the mainline
// expression emitter.
auto emitOperand = [&](Attribute operand) -> bool {
if (operandSign.hasValue())
os << (operandSign.getValue() == IsSigned ? "$signed(" : "$unsigned(");
auto signedness =
printParamValue(operand, os, subprecedence, emitError).signedness;
if (operandSign.hasValue()) {
os << ")";
signedness = operandSign.getValue();
}
return signedness == IsSigned;
};
if (subprecedence > parenthesizeIfLooserThan)
os << '(';
bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
for (auto op : ArrayRef(expr.getOperands()).drop_front()) {
os << operatorStr;
allOperandsSigned &= emitOperand(op);
}
if (subprecedence > parenthesizeIfLooserThan) {
os << ')';
subprecedence = Symbol;
}
return {subprecedence, allOperandsSigned ? IsSigned : IsUnsigned};
}
/// Prints a parameter attribute expression in a Verilog compatible way.
/// This returns the precedence of the generated string.
static SubExprInfo
printParamValue(Attribute value, raw_ostream &os,
function_ref<InFlightDiagnostic()> emitError) {
return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
emitError);
}
/// Return true for nullary operations that are better emitted multiple
/// times as inline expression (when they have multiple uses) rather than having
/// a temporary wire.
@ -703,7 +546,7 @@ StringRef ModuleNameManager::addName(ValueOrOp valueOrOp, StringRef name) {
}
//===----------------------------------------------------------------------===//
// VerilogEmitter
// VerilogEmitterState
//===----------------------------------------------------------------------===//
namespace {
@ -906,7 +749,16 @@ public:
void emitBind(BindOp op);
void emitBindInterface(BindInterfaceOp op);
/// Prints a parameter attribute expression in a Verilog compatible way to the
/// specified stream. This returns the precedence of the generated string.
SubExprInfo printParamValue(Attribute value, raw_ostream &os,
function_ref<InFlightDiagnostic()> emitError);
public:
SubExprInfo printParamValue(Attribute value, raw_ostream &os,
VerilogPrecedence parenthesizeIfLooserThan,
function_ref<InFlightDiagnostic()> emitError);
/// This set keeps track of all of the expression nodes that need to be
/// emitted as standalone wire declarations. This can happen because they are
/// multiply-used or because the user requires a name to reference.
@ -921,6 +773,163 @@ public:
} // end anonymous namespace
/// Prints a parameter attribute expression in a Verilog compatible way to the
/// specified stream. This returns the precedence of the generated string.
SubExprInfo
ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
function_ref<InFlightDiagnostic()> emitError) {
return printParamValue(value, os, VerilogPrecedence::LowestPrecedence,
emitError);
}
/// Helper that prints a parameter constant value in a Verilog compatible way.
/// This returns the precedence of the generated string.
SubExprInfo
ModuleEmitter::printParamValue(Attribute value, raw_ostream &os,
VerilogPrecedence parenthesizeIfLooserThan,
function_ref<InFlightDiagnostic()> emitError) {
if (auto intAttr = value.dyn_cast<IntegerAttr>()) {
IntegerType intTy = intAttr.getType().cast<IntegerType>();
APInt value = intAttr.getValue();
// We omit the width specifier if the value is <= 32-bits in size, which
// makes this more compatible with unknown width extmodules.
if (intTy.getWidth() > 32) {
// Sign comes out before any width specifier.
if (intTy.isSigned() && value.isNegative()) {
os << '-';
value = -value;
}
if (intTy.isSigned())
os << intTy.getWidth() << "'sd";
else
os << intTy.getWidth() << "'d";
}
value.print(os, intTy.isSigned());
return {Symbol, intTy.isSigned() ? IsSigned : IsUnsigned};
}
if (auto strAttr = value.dyn_cast<StringAttr>()) {
os << '"';
os.write_escaped(strAttr.getValue());
os << '"';
return {Symbol, IsUnsigned};
}
if (auto fpAttr = value.dyn_cast<FloatAttr>()) {
// TODO: relying on float printing to be precise is not a good idea.
os << fpAttr.getValueAsDouble();
return {Symbol, IsUnsigned};
}
if (auto verbatimParam = value.dyn_cast<ParamVerbatimAttr>()) {
os << verbatimParam.getValue().getValue();
return {Symbol, IsUnsigned};
}
if (auto parameterRef = value.dyn_cast<ParamDeclRefAttr>()) {
os << parameterRef.getName().getValue();
// TODO: Should we support signed parameters?
return {Symbol, IsUnsigned};
}
// Handle nested expressions.
auto expr = value.dyn_cast<ParamExprAttr>();
if (!expr) {
os << "<<UNKNOWN MLIRATTR: " << value << ">>";
emitError() << " = " << value;
return {LowestPrecedence, IsUnsigned};
}
StringRef operatorStr;
VerilogPrecedence subprecedence = ForceEmitMultiUse;
Optional<SubExprSignResult> operandSign;
switch (expr.getOpcode()) {
case PEO::Add:
operatorStr = " + ";
subprecedence = Addition;
break;
case PEO::Mul:
operatorStr = " * ";
subprecedence = Multiply;
break;
case PEO::And:
operatorStr = " & ";
subprecedence = And;
break;
case PEO::Or:
operatorStr = " | ";
subprecedence = Or;
break;
case PEO::Xor:
operatorStr = " ^ ";
subprecedence = Xor;
break;
case PEO::Shl:
operatorStr = " << ";
subprecedence = Shift;
break;
case PEO::ShrU:
// >> in verilog is always a logical shift even if operands are signed.
operatorStr = " >> ";
subprecedence = Shift;
break;
case PEO::ShrS:
// >>> in verilog is an arithmetic shift if both operands are signed.
operatorStr = " >>> ";
subprecedence = Shift;
operandSign = IsSigned;
break;
case PEO::DivU:
operatorStr = " / ";
subprecedence = Multiply;
operandSign = IsUnsigned;
break;
case PEO::DivS:
operatorStr = " / ";
subprecedence = Multiply;
operandSign = IsSigned;
break;
case PEO::ModU:
operatorStr = " % ";
subprecedence = Multiply;
operandSign = IsUnsigned;
break;
case PEO::ModS:
operatorStr = " % ";
subprecedence = Multiply;
operandSign = IsSigned;
break;
}
// Emit the specified operand with a $signed() or $unsigned() wrapper around
// it if context requires a specific signedness to compute the right value.
// This returns true if the operand is signed.
// TODO: This could try harder to omit redundant casts like the mainline
// expression emitter.
auto emitOperand = [&](Attribute operand) -> bool {
if (operandSign.hasValue())
os << (operandSign.getValue() == IsSigned ? "$signed(" : "$unsigned(");
auto signedness =
printParamValue(operand, os, subprecedence, emitError).signedness;
if (operandSign.hasValue()) {
os << ')';
signedness = operandSign.getValue();
}
return signedness == IsSigned;
};
if (subprecedence > parenthesizeIfLooserThan)
os << '(';
bool allOperandsSigned = emitOperand(expr.getOperands()[0]);
for (auto op : ArrayRef(expr.getOperands()).drop_front()) {
os << operatorStr;
allOperandsSigned &= emitOperand(op);
}
if (subprecedence > parenthesizeIfLooserThan) {
os << ')';
subprecedence = Symbol;
}
return {subprecedence, allOperandsSigned ? IsSigned : IsUnsigned};
}
//===----------------------------------------------------------------------===//
// Expression Emission
//===----------------------------------------------------------------------===//
@ -1514,7 +1523,7 @@ SubExprInfo ExprEmitter::visitTypeOp(ConstantOp op) {
}
SubExprInfo ExprEmitter::visitTypeOp(ParamValueOp op) {
return printParamValue(op.value(), os, [&]() {
return emitter.printParamValue(op.value(), os, [&]() {
return op->emitOpError("invalid parameter use");
});
}
@ -2680,7 +2689,7 @@ LogicalResult StmtEmitter::visitStmt(InstanceOp op) {
}
os.indent(state.currentIndent + INDENT_AMOUNT)
<< prefix << '.' << param.getName().getValue() << '(';
printParamValue(param.getValue(), os, [&]() {
emitter.printParamValue(param.getValue(), os, [&]() {
return op->emitOpError("invalid instance parameter '")
<< param.getName().getValue() << "' value";
});
@ -3019,7 +3028,7 @@ void StmtEmitter::collectNamesEmitDecls(Block &block) {
if (auto localparam = dyn_cast<LocalParamOp>(op)) {
os << " = ";
printParamValue(localparam.value(), os, [&]() {
emitter.printParamValue(localparam.value(), os, [&]() {
return op->emitOpError("invalid localparam value");
});
}