// // This file is distributed under the MIT License. See LICENSE.md for details. // #include "llvm/ADT/ScopeExit.h" #include "revng/ADT/RecursiveCoroutine.h" #include "revng/Clift/CliftOpHelpers.h" #include "revng/CliftEmitC/CBackend.h" #include "revng/CliftEmitC/CEmitter.h" #include "revng/PTML/CTokenEmitter.h" using namespace clift; namespace { static RecursiveCoroutine noopCoroutine() { rc_return; } static bool hasFallthrough(mlir::Region &R) { return not getLastNoFallthroughStatement(R); } enum class OperatorPrecedence { Parentheses, Comma, Assignment, Or, And, Bitor, Bitxor, Bitand, Equality, Relational, Shift, Additive, Multiplicative, UnaryPrefix, UnaryPostfix, Primary, Ternary = Assignment, }; class CliftToCEmitter : CEmitter { // Ambient precedence of the current expression. OperatorPrecedence CurrentPrecedence = {}; public: using CEmitter::CEmitter; static OperatorPrecedence decrementPrecedence(OperatorPrecedence Precedence) { revng_assert(Precedence != static_cast(0)); using T = std::underlying_type_t; return static_cast(static_cast(Precedence) - 1); } void emitCStyleCast(mlir::Type Type) { Tokens.emitOperator(CTE::Operator::LeftParenthesis); emitType(Type); Tokens.emitOperator(CTE::Operator::RightParenthesis); Tokens.emitSpace(); } void emitEnumImmediate(uint64_t Value, EnumType Type) { auto Enumerator = Type.getFieldByValue(Value); revng_assert(Enumerator); Tokens.emitIdentifier(Enumerator.getName(), Enumerator.getHandle(), CTE::EntityKind::Enumerator, CTE::IdentifierKind::Reference); } void emitIntegerLiteral(uint64_t Value, bool IsSigned, CStandardType Type, unsigned Radix) { if (IsSigned and static_cast(Value) < 0) { Tokens.emitOperator(ptml::CTokenEmitter::Operator::Minus); Value = ~Value + 1; } Tokens.emitIntegerLiteral(llvm::APInt(64, Value, IsSigned), CTE::IntegerSuffix{ .Unsigned = not IsSigned, .MinimumType = Type }, Radix); } //===---------------------------- Expressions ---------------------------===// RecursiveCoroutine emitUndefExpression(mlir::Value V) { revng_assert(isScalarType(V.getType())); Tokens.emitLiteralIdentifier("undef"); Tokens.emitOperator(CTE::Operator::LeftParenthesis); emitType(V.getType()); Tokens.emitOperator(CTE::Operator::RightParenthesis); rc_return; } static bool isNullPointerConstant(ImmediateOp E) { if (E.getValue() != 0) return false; if (auto Cast = getOnlyUser(E)) return clift::unwrapped_isa(Cast.getResult().getType()); return false; } RecursiveCoroutine emitImmediateExpression(mlir::Value V) { auto E = V.getDefiningOp(); mlir::Type Type = unwrapTypedefs(E.getType()); if (auto Enum = mlir::dyn_cast(Type)) rc_return emitEnumImmediate(E.getValue(), Enum); unsigned Radix = 10; if (auto Attr = E->getAttr("clift.radix")) Radix = mlir::cast(Attr).getValue().getZExtValue(); auto IntType = mlir::cast(Type); auto CType = CStandardType::Int; // Using any specific integer suffix is only required when the value is not // immediately converted to another integer type. While such casts are // usually removed by expression rewriting, some may be reintroduced during // legalization. auto Cast = getOnlyUser(V); if (not Cast or not unwrapped_isa(Cast.getResult().getType())) { auto Range = DataModel.getStandardIntegerRange(IntType.getSize()); revng_assert(Range, "Integer immediate not representable in C."); CType = Range->first; } emitIntegerLiteral(E.getValue(), IntType.isSigned(), CType, Radix); rc_return; } RecursiveCoroutine emitNullPointerConstant(mlir::Value V) { Tokens.emitLiteralIdentifier("NULL"); rc_return; } RecursiveCoroutine emitStringLiteralExpression(mlir::Value V) { auto E = V.getDefiningOp(); Tokens.emitStringLiteral(E.getValue()); rc_return; } RecursiveCoroutine emitAggregateInitializer(AggregateOp E) { // The precedence here must be comma, because an initializer list cannot // contain an unparenthesized comma expression. It would be parsed as two // initializers instead. CurrentPrecedence = OperatorPrecedence::Comma; Tokens.emitPunctuator(CTE::Punctuator::LeftBrace); for (auto [I, Initializer] : llvm::enumerate(E.getInitializers())) { if (I != 0) { Tokens.emitPunctuator(CTE::Punctuator::Comma); Tokens.emitSpace(); } rc_recur emitExpression(Initializer); } Tokens.emitPunctuator(CTE::Punctuator::RightBrace); } RecursiveCoroutine emitAggregateExpression(mlir::Value V) { auto E = V.getDefiningOp(); Tokens.emitOperator(CTE::Operator::LeftParenthesis); emitType(E.getResult().getType()); Tokens.emitOperator(CTE::Operator::RightParenthesis); rc_recur emitAggregateInitializer(E); } RecursiveCoroutine emitBlockArgumentExpression(mlir::Value V) { auto E = mlir::cast(V); mlir::Operation *Op = E.getOwner()->getParentOp(); if (auto Function = mlir::dyn_cast(Op)) { const auto &Attrs = Function.getArgAttrs(E.getArgNumber()); revng_assert(Attrs.getOfType("clift.name"), "Function argument name (clift.name) is missing."); Tokens.emitIdentifier(Attrs.getString("clift.name"), Attrs.getStringOrEmpty("clift.handle"), CTE::EntityKind::FunctionParameter, CTE::IdentifierKind::Reference); } else if (auto For = mlir::dyn_cast(Op)) { auto Local = getOnlyOp(For.getInitializer()); rc_recur emitLocalVariableExpression(Local.getResult()); } } RecursiveCoroutine emitLocalVariableExpression(mlir::Value V) { auto E = V.getDefiningOp(); Tokens.emitIdentifier(E.getName(), E.getHandle(), CTE::EntityKind::LocalVariable, CTE::IdentifierKind::Reference); rc_return; } RecursiveCoroutine emitUseExpression(mlir::Value V) { auto E = V.getDefiningOp(); auto Module = E->getParentOfType(); revng_assert(Module); auto S = mlir::SymbolTable::lookupSymbolIn(Module, E.getSymbolNameAttr()); auto Symbol = mlir::cast(S); constexpr auto GetEntityKind = [](GlobalOpInterface Symbol) { if (mlir::isa(Symbol)) return CTE::EntityKind::Function; if (mlir::isa(Symbol)) return CTE::EntityKind::GlobalVariable; revng_abort("Unsupported global operation"); }; Tokens.emitIdentifier(Symbol.getName(), Symbol.getHandle(), GetEntityKind(Symbol), CTE::IdentifierKind::Reference); rc_return; } RecursiveCoroutine emitAccessExpression(mlir::Value V) { auto E = V.getDefiningOp(); // Parenthesizing a nested unary postfix expression is not necessary. CurrentPrecedence = decrementPrecedence(OperatorPrecedence::UnaryPostfix); rc_recur emitExpression(E.getValue()); Tokens.emitOperator(E.isIndirect() ? CTE::Operator::Arrow : CTE::Operator::Dot); auto Field = E.getClassType().getFields()[E.getMemberIndex()]; Tokens.emitIdentifier(Field.getName(), Field.getHandle(), CTE::EntityKind::Field, CTE::IdentifierKind::Reference); } RecursiveCoroutine emitSubscriptExpression(mlir::Value V) { auto E = V.getDefiningOp(); // Parenthesizing a nested unary postfix expression is not necessary. CurrentPrecedence = decrementPrecedence(OperatorPrecedence::UnaryPostfix); rc_recur emitExpression(E.getPointer()); // The precedence here could be parentheses and still preserve semantics, // but given that a comma expression within a subscript ( array[i, j] ) is // not only very confusing, but has a different meaning in C++23, we force // comma expressions to be parenthesized, the same way they are in argument // lists. The output in this case is as: array[(i, j)] CurrentPrecedence = OperatorPrecedence::Comma; Tokens.emitOperator(CTE::Operator::LeftBracket); rc_recur emitExpression(E.getIndex()); Tokens.emitOperator(CTE::Operator::RightBracket); } RecursiveCoroutine emitCallExpression(mlir::Value V) { auto E = V.getDefiningOp(); // Parenthesizing a nested unary postfix expression is not necessary. CurrentPrecedence = decrementPrecedence(OperatorPrecedence::UnaryPostfix); rc_recur emitExpression(E.getFunction()); // The precedence here must be comma, because an argument list cannot // contain an unparenthesized comma expression. It would be parsed as two // arguments instead. CurrentPrecedence = OperatorPrecedence::Comma; Tokens.emitOperator(CTE::Operator::LeftParenthesis); for (auto [I, A] : llvm::enumerate(E.getArguments())) { if (I != 0) { Tokens.emitPunctuator(CTE::Punctuator::Comma); Tokens.emitSpace(); } rc_recur emitExpression(A); } Tokens.emitOperator(CTE::Operator::RightParenthesis); } static bool isHiddenCast(CastOpInterface Cast) { return mlir::isa(Cast) or Cast->hasAttr("clift.implicit"); } static mlir::Value unwrapHiddenCasts(CastOpInterface Cast) { revng_assert(isHiddenCast(Cast)); while (true) { auto Inner = Cast.getValue().getDefiningOp(); if (not Inner or not isHiddenCast(Inner)) break; } return Cast.getValue(); } static bool requiresExplicitBitCast(BitCastOp Op) { auto IsCastableType = [](mlir::Type T) { return clift::unwrapped_isa(T); }; return not IsCastableType(Op.getValue().getType()) or not IsCastableType(Op.getResult().getType()); } RecursiveCoroutine emitBitCastExpression(mlir::Value V) { auto E = V.getDefiningOp(); Tokens.emitLiteralIdentifier("bit_cast"); Tokens.emitPunctuator(CTE::Punctuator::LeftParenthesis); emitType(E.getResult().getType()); Tokens.emitPunctuator(CTE::Punctuator::Comma); Tokens.emitSpace(); CurrentPrecedence = OperatorPrecedence::Parentheses; rc_recur emitExpression(E.getValue()); Tokens.emitPunctuator(CTE::Punctuator::RightParenthesis); } RecursiveCoroutine emitCastExpression(mlir::Value V) { auto E = V.getDefiningOp(); emitCStyleCast(E.getResult().getType()); // Parenthesizing a nested unary prefix expression is not necessary. CurrentPrecedence = decrementPrecedence(OperatorPrecedence::UnaryPrefix); rc_recur emitExpression(E.getValue()); } RecursiveCoroutine emitHiddenCastExpression(mlir::Value V) { auto E = V.getDefiningOp(); return emitExpression(unwrapHiddenCasts(E)); } RecursiveCoroutine emitTernaryExpression(mlir::Value V) { auto E = V.getDefiningOp(); rc_recur emitExpression(E.getCondition()); Tokens.emitSpace(); Tokens.emitOperator(CTE::Operator::Question); Tokens.emitSpace(); rc_recur emitExpression(E.getLhs()); Tokens.emitSpace(); Tokens.emitOperator(CTE::Operator::Colon); Tokens.emitSpace(); // The right hand expression does not need parentheses. CurrentPrecedence = decrementPrecedence(OperatorPrecedence::Ternary); rc_recur emitExpression(E.getRhs()); } static CTE::Operator getOperator(mlir::Operation *Op) { if (mlir::isa(Op)) return CTE::Operator::Minus; if (mlir::isa(Op)) return CTE::Operator::Plus; if (mlir::isa(Op)) return CTE::Operator::Star; if (mlir::isa(Op)) return CTE::Operator::Slash; if (mlir::isa(Op)) return CTE::Operator::Percent; if (mlir::isa(Op)) return CTE::Operator::Exclaim; if (mlir::isa(Op)) return CTE::Operator::AmpersandAmpersand; if (mlir::isa(Op)) return CTE::Operator::PipePipe; if (mlir::isa(Op)) return CTE::Operator::Tilde; if (mlir::isa(Op)) return CTE::Operator::Ampersand; if (mlir::isa(Op)) return CTE::Operator::Pipe; if (mlir::isa(Op)) return CTE::Operator::Caret; if (mlir::isa(Op)) return CTE::Operator::LessLess; if (mlir::isa(Op)) return CTE::Operator::GreaterGreater; if (mlir::isa(Op)) return CTE::Operator::EqualsEquals; if (mlir::isa(Op)) return CTE::Operator::ExclaimEquals; if (mlir::isa(Op)) return CTE::Operator::Less; if (mlir::isa(Op)) return CTE::Operator::Greater; if (mlir::isa(Op)) return CTE::Operator::LessEquals; if (mlir::isa(Op)) return CTE::Operator::GreaterEquals; if (mlir::isa(Op)) return CTE::Operator::PlusPlus; if (mlir::isa(Op)) return CTE::Operator::MinusMinus; if (mlir::isa(Op)) return CTE::Operator::Equals; if (mlir::isa(Op)) return CTE::Operator::Comma; revng_abort("This operation does not represent a C operator."); } RecursiveCoroutine emitPrefixExpression(mlir::Value V) { mlir::Operation *Op = V.getDefiningOp(); mlir::Value Operand = Op->getOperand(0); Tokens.emitOperator(getOperator(Op)); auto StartsWithMinus = [](mlir::Value V) { if (mlir::isa(V.getDefiningOp())) return true; if (auto I = V.getDefiningOp()) { if (auto T = mlir::dyn_cast(I.getResult().getType())) return T.isSigned() and static_cast(I.getValue()) < 0; } return false; }; // Double negation requires a space in between to avoid being confused as // decrement. (- -x) vs (--x) // // Negation after a decrement requires a space in between to avoid being // confused as decrement after negation. (- --x) vs (---x) if (V.getDefiningOp() and StartsWithMinus(Operand)) Tokens.emitSpace(); // Parenthesizing a nested unary prefix expression is not necessary. CurrentPrecedence = decrementPrecedence(OperatorPrecedence::UnaryPrefix); return emitExpression(Operand); } RecursiveCoroutine emitPostfixExpression(mlir::Value V) { mlir::Operation *Op = V.getDefiningOp(); rc_recur emitExpression(Op->getOperand(0)); // Parenthesizing a nested unary postfix expression is not necessary. CurrentPrecedence = decrementPrecedence(OperatorPrecedence::UnaryPostfix); Tokens.emitOperator(getOperator(Op)); } RecursiveCoroutine emitInfixExpression(mlir::Value V) { mlir::Operation *Op = V.getDefiningOp(); auto LhsPrecedence = decrementPrecedence(CurrentPrecedence); auto RhsPrecedence = CurrentPrecedence; // Assignment operators are right-associative. if (CurrentPrecedence == OperatorPrecedence::Assignment) std::swap(LhsPrecedence, RhsPrecedence); CurrentPrecedence = LhsPrecedence; rc_recur emitExpression(Op->getOperand(0)); if (not mlir::isa(Op)) Tokens.emitSpace(); Tokens.emitOperator(getOperator(Op)); Tokens.emitSpace(); CurrentPrecedence = RhsPrecedence; rc_recur emitExpression(Op->getOperand(1)); } struct ExpressionEmitInfo { OperatorPrecedence Precedence; RecursiveCoroutine (CliftToCEmitter::*Emit)(mlir::Value V); }; // This function handles the dispatching for emitting different kinds of // expressions. It returns the precedence of the expression and a pointer to // a member function used for emitting it. The actual emission is only handled // afterwards. The reason for this is that the precedence must be known before // we start emitting the expression, because it may need to parenthesized. static ExpressionEmitInfo getExpressionEmitInfo(mlir::Value V) { auto E = V.getDefiningOp(); if (not E) { if (mlir::isa(V)) { return { .Precedence = OperatorPrecedence::Primary, .Emit = &CliftToCEmitter::emitBlockArgumentExpression, }; } if (auto Variable = V.getDefiningOp()) { return { .Precedence = OperatorPrecedence::Primary, .Emit = &CliftToCEmitter::emitLocalVariableExpression, }; } revng_abort("This operation is not supported."); } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::Primary, .Emit = &CliftToCEmitter::emitUndefExpression, }; } if (auto Immediate = mlir::dyn_cast(E.getOperation())) { if (isNullPointerConstant(Immediate)) { return { .Precedence = OperatorPrecedence::Primary, .Emit = &CliftToCEmitter::emitNullPointerConstant, }; } return { .Precedence = OperatorPrecedence::Primary, .Emit = &CliftToCEmitter::emitImmediateExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::Primary, .Emit = &CliftToCEmitter::emitStringLiteralExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::Primary, .Emit = &CliftToCEmitter::emitAggregateExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::Primary, .Emit = &CliftToCEmitter::emitUseExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::UnaryPostfix, .Emit = &CliftToCEmitter::emitAccessExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::UnaryPostfix, .Emit = &CliftToCEmitter::emitSubscriptExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::UnaryPostfix, .Emit = &CliftToCEmitter::emitCallExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::UnaryPostfix, .Emit = &CliftToCEmitter::emitPostfixExpression, }; } if (auto Cast = mlir::dyn_cast(E.getOperation())) { if (isHiddenCast(Cast)) { auto Info = getExpressionEmitInfo(unwrapHiddenCasts(Cast)); return { .Precedence = decrementPrecedence(Info.Precedence), .Emit = &CliftToCEmitter::emitHiddenCastExpression, }; } if (auto BitCast = mlir::dyn_cast(E.getOperation())) { if (requiresExplicitBitCast(BitCast)) { return { .Precedence = OperatorPrecedence::UnaryPostfix, .Emit = &CliftToCEmitter::emitBitCastExpression, }; } } return { .Precedence = OperatorPrecedence::UnaryPrefix, .Emit = &CliftToCEmitter::emitCastExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::UnaryPrefix, .Emit = &CliftToCEmitter::emitPrefixExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::Multiplicative, .Emit = &CliftToCEmitter::emitInfixExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::Additive, .Emit = &CliftToCEmitter::emitInfixExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::Shift, .Emit = &CliftToCEmitter::emitInfixExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::Relational, .Emit = &CliftToCEmitter::emitInfixExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::Equality, .Emit = &CliftToCEmitter::emitInfixExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::Bitand, .Emit = &CliftToCEmitter::emitInfixExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::Bitxor, .Emit = &CliftToCEmitter::emitInfixExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::Bitor, .Emit = &CliftToCEmitter::emitInfixExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::And, .Emit = &CliftToCEmitter::emitInfixExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::Or, .Emit = &CliftToCEmitter::emitInfixExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::Assignment, .Emit = &CliftToCEmitter::emitInfixExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::Comma, .Emit = &CliftToCEmitter::emitInfixExpression, }; } if (mlir::isa(E)) { return { .Precedence = OperatorPrecedence::Ternary, .Emit = &CliftToCEmitter::emitTernaryExpression, }; } revng_abort("This operation is not supported."); } static std::optional getExpressionLocation(mlir::Value V) { if (auto E = V.getDefiningOp()) { if (auto NameLoc = mlir::dyn_cast_or_null(E->getLoc())) return NameLoc.getName(); } return std::nullopt; } RecursiveCoroutine emitExpression(mlir::Value V) { const ExpressionEmitInfo Info = getExpressionEmitInfo(V); bool PrintParentheses = Info.Precedence <= CurrentPrecedence and Info.Precedence != OperatorPrecedence::Primary; if (PrintParentheses) Tokens.emitPunctuator(CTE::Punctuator::LeftParenthesis); // CurrentPrecedence is changed within this scope: { const auto PreviousPrecedence = CurrentPrecedence; const auto PrecedenceGuard = llvm::make_scope_exit([&]() { CurrentPrecedence = PreviousPrecedence; }); CurrentPrecedence = Info.Precedence; // If an expression location is available, within this scope an expression // region is entered. std::optional Region; if (auto Location = getExpressionLocation(V)) Region.emplace(Tokens, CTE::RegionKind::Expression, *Location); // Emit the expression using the member function returned by // getExpressionEmitInfo. rc_recur(this->*Info.Emit)(V); } if (PrintParentheses) Tokens.emitPunctuator(CTE::Punctuator::RightParenthesis); } RecursiveCoroutine emitExpressionRegion(mlir::Region &R) { mlir::Value Value = getExpressionValue(R); revng_assert(Value); return emitExpression(Value); } //===---------------------------- Statements ----------------------------===// RecursiveCoroutine emitLocalVariableDeclaration(LocalVariableOp S, bool EmitNewline) { emitDeclaration(S.getResult().getType(), DeclaratorInfo{ .Identifier = S.getName(), .Location = S.getHandle(), .CAttributes = getDeclarationOpCAttributes(S), .Kind = CTE::EntityKind::LocalVariable, }); if (not S.getInitializer().empty()) { Tokens.emitSpace(); Tokens.emitOperator(CTE::Operator::Equals); Tokens.emitSpace(); // Comma expressions in a variable initialiser must be parenthesized. CurrentPrecedence = OperatorPrecedence::Comma; mlir::Value Expression = getExpressionValue(S.getInitializer()); if (auto Aggregate = Expression.getDefiningOp()) rc_recur emitAggregateInitializer(Aggregate); else rc_recur emitExpression(Expression); } Tokens.emitPunctuator(CTE::Punctuator::Semicolon); if (EmitNewline) Tokens.emitNewline(); } bool labelRequiresEmptyExpression(LabelAssignmentOpInterface Op) { // Prior to C23, labels cannot be placed at the end of a block: if (Op.getOperation() == &Op->getBlock()->back()) return true; // Prior to C23, labels cannot be placed preceding a declaration: if (mlir::isa(&*std::next(Op->getIterator()))) return true; return false; } void emitLabelStatementImpl(MakeLabelOp Label, bool RequiresEmptyExpression) { auto Scope = Tokens.enterScope(CTE::ScopeKind::None, CTE::Delimiter::None, /*Indent=*/-1); Tokens.emitIdentifier(Label.getName(), Label.getHandle(), CTE::EntityKind::Label, CTE::IdentifierKind::Definition); Tokens.emitPunctuator(CTE::Punctuator::Colon); if (RequiresEmptyExpression) { Tokens.emitSpace(); Tokens.emitPunctuator(CTE::Punctuator::Semicolon); } Tokens.emitNewline(); } void emitLabelStatement(MakeLabelOp Label, LabelAssignmentOpInterface Op) { emitLabelStatementImpl(Label, labelRequiresEmptyExpression(Op)); } RecursiveCoroutine emitLabelStatement(AssignLabelOp S) { emitLabelStatement(S.getLabelOp(), LabelAssignmentOpInterface(S)); rc_return; } RecursiveCoroutine emitExpressionStatement(ExpressionStatementOp S) { rc_recur emitExpressionRegion(S.getExpression()); Tokens.emitPunctuator(CTE::Punctuator::Semicolon); Tokens.emitNewline(); } RecursiveCoroutine emitLabeledJumpStatement(JumpStatementOpInterface S) { auto LabelOp = S.getLabel().getDefiningOp(); if (mlir::isa(S)) Tokens.emitKeyword(CTE::Keyword::Goto); else if (mlir::isa(S)) Tokens.emitLiteralIdentifier("break_to"); else if (mlir::isa(S)) Tokens.emitLiteralIdentifier("continue_to"); else revng_abort("Unsupported jump statement"); Tokens.emitSpace(); Tokens.emitIdentifier(LabelOp.getName(), LabelOp.getHandle(), CTE::EntityKind::Label, CTE::IdentifierKind::Reference); Tokens.emitPunctuator(CTE::Punctuator::Semicolon); Tokens.emitNewline(); rc_return; } RecursiveCoroutine emitReturnStatement(ReturnOp S) { Tokens.emitKeyword(CTE::Keyword::Return); if (not S.getResult().empty()) { Tokens.emitSpace(); rc_recur emitExpressionRegion(S.getResult()); } Tokens.emitPunctuator(CTE::Punctuator::Semicolon); Tokens.emitNewline(); } static bool mayElideIfStatementBraces(IfOp If) { while (true) { if (not mayElideBraces(If.getThen())) return false; if (If.getElse().empty()) return true; auto ElseIf = getOnlyOp(If.getElse()); if (not ElseIf) return mayElideBraces(If.getElse()); If = ElseIf; } } RecursiveCoroutine emitIfStatement(IfOp S) { // Nested if-else-if chains are printed out in a loop to avoid introducing // extra indentation for each else-if. bool EmitBlocks = not mayElideIfStatementBraces(S); while (true) { Tokens.emitKeyword(CTE::Keyword::If); Tokens.emitSpace(); Tokens.emitPunctuator(CTE::Punctuator::LeftParenthesis); rc_recur emitExpressionRegion(S.getCondition()); Tokens.emitPunctuator(CTE::Punctuator::RightParenthesis); rc_recur emitImplicitBlockStatement(S.getThen(), EmitBlocks); if (S.getElse().empty()) break; if (EmitBlocks) Tokens.emitSpace(); Tokens.emitKeyword(CTE::Keyword::Else); if (auto ElseIf = getOnlyOp(S.getElse())) { S = ElseIf; Tokens.emitSpace(); } else { rc_recur emitImplicitBlockStatement(S.getElse(), EmitBlocks); break; } } if (EmitBlocks) Tokens.emitNewline(); } RecursiveCoroutine emitCaseRegion(mlir::Region &R) { bool Break = hasFallthrough(R); if (rc_recur emitImplicitBlockStatement(R)) { if (Break) Tokens.emitSpace(); else Tokens.emitNewline(); } if (Break) { Tokens.emitKeyword(CTE::Keyword::Break); Tokens.emitPunctuator(CTE::Punctuator::Semicolon); Tokens.emitNewline(); } } class CaseValueEmitter { CliftToCEmitter &Parent; mlir::Type Type; EnumType Enum; bool IsSigned; unsigned Radix; public: explicit CaseValueEmitter(CliftToCEmitter &Parent, mlir::Type Type, unsigned Radix) : CaseValueEmitter(Parent, Type, unwrapTypedefs(Type), Radix) {} void emit(uint64_t Value) const { if (Enum) { if (auto Enumerator = Enum.getFieldByValue(Value)) return Parent.emitEnumImmediate(Value, Enum); Parent.emitCStyleCast(Type); } Parent.emitIntegerLiteral(Value, IsSigned, CStandardType::Int, Radix); } private: explicit CaseValueEmitter(CliftToCEmitter &Parent, mlir::Type Type, mlir::Type UnwrappedType, unsigned Radix) : Parent(Parent), Type(Type), Enum(mlir::dyn_cast(UnwrappedType)), IsSigned(getUnderlyingIntegerType(UnwrappedType).isSigned()), Radix(Radix) {} }; RecursiveCoroutine emitSwitchStatement(SwitchOp S) { unsigned Radix = 10; if (auto Attr = S->getAttr("clift.radix")) Radix = mlir::cast(Attr).getValue().getZExtValue(); Tokens.emitKeyword(CTE::Keyword::Switch); Tokens.emitSpace(); Tokens.emitPunctuator(CTE::Punctuator::LeftParenthesis); rc_recur emitExpressionRegion(S.getCondition()); Tokens.emitPunctuator(CTE::Punctuator::RightParenthesis); Tokens.emitSpace(); // Scope tags are applied within this scope: { auto Scope = Tokens.enterScope(CTE::ScopeKind::BlockStatement, CTE::Delimiter::Braces, /*Indented=*/false); Tokens.emitNewline(); CaseValueEmitter CVE(*this, S.getConditionType(), Radix); for (unsigned I = 0, Count = S.getNumCases(); I < Count; ++I) { Tokens.emitKeyword(CTE::Keyword::Case); Tokens.emitSpace(); CVE.emit(S.getCaseValue(I)); Tokens.emitPunctuator(CTE::Punctuator::Colon); rc_recur emitCaseRegion(S.getCaseRegion(I)); } if (S.hasDefaultCase()) { Tokens.emitKeyword(CTE::Keyword::Default); Tokens.emitPunctuator(CTE::Punctuator::Colon); rc_recur emitCaseRegion(S.getDefaultCaseRegion()); } } Tokens.emitNewline(); } RecursiveCoroutine emitLoopBodyWithContinueLabel(mlir::Region &Region, MakeLabelOp Continue) { auto Emit = [this, Continue](mlir::Region &R) -> RecursiveCoroutine { rc_recur emitStatementRegion(R); emitLabelStatementImpl(Continue, /*RequiresEmptyExpression=*/true); }; return emitImplicitBlockStatement(Region, true, Emit); } RecursiveCoroutine emitLoopBody(LoopOpInterface Loop, mlir::Region &Region) { if (auto Continue = Loop.getContinueLabel()) { auto Label = Continue.getDefiningOp(); return emitLoopBodyWithContinueLabel(Region, Label); } return emitImplicitBlockStatement(Region); } RecursiveCoroutine emitForStatement(ForOp S) { Tokens.emitKeyword(CTE::Keyword::For); Tokens.emitSpace(); Tokens.emitPunctuator(CTE::Punctuator::LeftParenthesis); if (mlir::Region &R = S.getInitializer(); not R.empty()) { mlir::Operation *Op = getOnlyOp(R); if (auto L = mlir::dyn_cast(Op)) rc_recur emitLocalVariableDeclaration(L, /*Newline=*/false); else rc_recur emitExpressionStatement(mlir::cast(Op)); } else { Tokens.emitPunctuator(CTE::Punctuator::Semicolon); } if (mlir::Region &R = S.getCondition(); not R.empty()) { Tokens.emitSpace(); rc_recur emitExpressionRegion(R); } Tokens.emitPunctuator(CTE::Punctuator::Semicolon); if (mlir::Region &R = S.getExpression(); not R.empty()) { Tokens.emitSpace(); rc_recur emitExpressionRegion(R); } Tokens.emitPunctuator(CTE::Punctuator::RightParenthesis); if (rc_recur emitLoopBody(S, S.getBody())) Tokens.emitNewline(); if (auto Break = S.getBreakLabel()) emitLabelStatement(Break.getDefiningOp(), S); } RecursiveCoroutine emitWhileStatement(WhileOp S) { Tokens.emitKeyword(CTE::Keyword::While); Tokens.emitSpace(); Tokens.emitPunctuator(CTE::Punctuator::LeftParenthesis); rc_recur emitExpressionRegion(S.getCondition()); Tokens.emitPunctuator(CTE::Punctuator::RightParenthesis); if (rc_recur emitLoopBody(S, S.getBody())) Tokens.emitNewline(); if (auto Break = S.getBreakLabel()) emitLabelStatement(Break.getDefiningOp(), S); } RecursiveCoroutine emitDoWhileStatement(DoWhileOp S) { Tokens.emitKeyword(CTE::Keyword::Do); if (rc_recur emitLoopBody(S, S.getBody())) Tokens.emitSpace(); Tokens.emitKeyword(CTE::Keyword::While); Tokens.emitSpace(); Tokens.emitPunctuator(CTE::Punctuator::LeftParenthesis); rc_recur emitExpressionRegion(S.getCondition()); Tokens.emitPunctuator(CTE::Punctuator::RightParenthesis); Tokens.emitPunctuator(CTE::Punctuator::Semicolon); Tokens.emitNewline(); if (auto Break = S.getBreakLabel()) emitLabelStatement(Break.getDefiningOp(), S); } RecursiveCoroutine emitBlockStatement(BlockStatementOp S) { { auto Scope = Tokens.enterScope(CTE::ScopeKind::BlockStatement, CTE::Delimiter::Braces); Tokens.emitNewline(); rc_recur emitStatementRegion(S.getBlock()); } Tokens.emitNewline(); } static mlir::ArrayAttr getComments(StatementOpInterface S) { return mlir::cast_or_null(S->getAttr("clift.comments")); } RecursiveCoroutine emitStatement(StatementOpInterface Stmt) { mlir::Operation *Op = Stmt.getOperation(); if (auto Comments = getComments(Stmt)) { // TODO: Add a comment formatting layer on top of CE. // At least spaces at the start of each line would be nice. auto CE = Tokens.emitComment(CTE::CommentKind::Line); for (mlir::Attribute CommentAttr : Comments) { CE.emit(mlir::cast(CommentAttr).getValue()); CE.emit("\n"); } } if (auto S = mlir::dyn_cast(Op)) return emitLocalVariableDeclaration(S, /*Newline=*/true); if (auto S = mlir::dyn_cast(Op)) return noopCoroutine(); if (auto S = mlir::dyn_cast(Op)) return emitLabelStatement(S); if (auto S = mlir::dyn_cast(Op)) return emitExpressionStatement(S); if (auto S = mlir::dyn_cast(Op)) return emitLabeledJumpStatement(S); if (auto S = mlir::dyn_cast(Op)) return noopCoroutine(); if (auto S = mlir::dyn_cast(Op)) return emitReturnStatement(S); if (auto S = mlir::dyn_cast(Op)) return emitIfStatement(S); if (auto S = mlir::dyn_cast(Op)) return emitSwitchStatement(S); if (auto S = mlir::dyn_cast(Op)) return emitForStatement(S); if (auto S = mlir::dyn_cast(Op)) return emitWhileStatement(S); if (auto S = mlir::dyn_cast(Op)) return emitDoWhileStatement(S); if (auto S = mlir::dyn_cast(Op)) return emitBlockStatement(S); revng_abort("Unsupported operation"); } RecursiveCoroutine emitStatementRegion(mlir::Region &R) { for (mlir::Operation &Stmt : R.getOps()) rc_recur emitStatement(mlir::cast(&Stmt)); } static bool mayElideBraces(mlir::Operation *Operation) { return mlir::isa(Operation); } static bool mayElideBraces(mlir::Region &R) { mlir::Operation *OnlyOp = getOnlyOp(R); return OnlyOp != nullptr and mayElideBraces(OnlyOp); } RecursiveCoroutine emitImplicitBlockStatement(mlir::Region &R, bool EmitBlock, auto EmitRegion) { auto ScopeKind = CTE::ScopeKind::None; auto Delimiter = CTE::Delimiter::None; if (EmitBlock) { Tokens.emitSpace(); ScopeKind = CTE::ScopeKind::BlockStatement; Delimiter = CTE::Delimiter::Braces; } auto Scope = Tokens.enterScope(ScopeKind, Delimiter); Tokens.emitNewline(); auto ExplicitBlock = getOnlyOp(R); rc_recur EmitRegion(ExplicitBlock ? ExplicitBlock.getBlock() : R); rc_return EmitBlock; } RecursiveCoroutine emitImplicitBlockStatement(mlir::Region &R, bool EmitBlock) { return emitImplicitBlockStatement(R, EmitBlock, [this](mlir::Region &R) { return emitStatementRegion(R); }); } RecursiveCoroutine emitImplicitBlockStatement(mlir::Region &R) { return emitImplicitBlockStatement(R, not mayElideBraces(R)); } //===----------------------------- Functions ----------------------------===// RecursiveCoroutine emitFunction(FunctionOp Op) { // Scope tags are applied within this scope: { auto OuterScope = Tokens.enterScope(CTE::ScopeKind::FunctionDeclaration, CTE::Delimiter::None, /*Indented=*/false); emitFunctionPrototype(Op); Tokens.emitSpace(); auto InnerScope = Tokens.enterScope(CTE::ScopeKind::FunctionDefinition, CTE::Delimiter::Braces); Tokens.emitNewline(); // TODO: Re-enable stack frame inlining. rc_recur emitStatementRegion(Op.getBody()); // TODO: emit a comment containing homeless variable names. // See how old backend does it for reference. } Tokens.emitNewline(); } }; } // namespace void decompile(FunctionOp Function, ptml::CTokenEmitter &Emitter) { CliftToCEmitter(Emitter, getDataModel(Function)).emitFunction(Function); }