// // This file is distributed under the MIT License. See LICENSE.md for details. // #include "llvm/ADT/ScopeExit.h" #include "revng/Clift/CliftOpHelpers.h" #include "revng/CliftEmitC/CBackend.h" #include "revng/CliftEmitC/CEmitter.h" namespace clift = mlir::clift; using namespace mlir::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; llvm::StringRef getStringAttr(mlir::Operation *Op, llvm::StringRef Name) { return mlir::cast(Op->getAttr(Name)).getValue(); } llvm::StringRef getNameAttr(mlir::Operation *Op) { return getStringAttr(Op, "clift.name"); } llvm::StringRef getLocationAttr(mlir::Operation *Op) { return getStringAttr(Op, "clift.handle"); } static OperatorPrecedence decrementPrecedence(OperatorPrecedence Precedence) { revng_assert(Precedence != static_cast(0)); using T = std::underlying_type_t; return static_cast(static_cast(Precedence) - 1); } static llvm::APSInt makeIntegerValue(PrimitiveType Type, uint64_t Value) { bool Signed = Type.getKind() == PrimitiveKind::SignedKind; return llvm::APSInt(llvm::APInt(Type.getSize() * 8, Value, Signed), not Signed); } void emitCast(ValueType Type) { C.emitOperator(CTE::Operator::LeftParenthesis); emitType(Type); C.emitOperator(CTE::Operator::RightParenthesis); C.emitSpace(); } void emitIntegerImmediate(uint64_t Value, ValueType Type) { Type = dealias(Type, /*IgnoreQualifiers=*/true); if (auto T = mlir::dyn_cast(Type)) { auto Integer = Target.getIntegerKind(T.getSize()); if (not Integer) { // Emit explicit cast if the standard integer type is not known. Emit // the literal itself without a suffix (as if int). emitCast(T); Integer = CIntegerKind::Int; } C.emitIntegerLiteral(makeIntegerValue(T, Value), *Integer, 10); } else { auto Enum = mlir::cast(Type); auto Enumerator = Enum.getFieldByValue(Value); C.emitIdentifier(Enumerator.getName(), Enumerator.getHandle(), CTE::EntityKind::Enumerator, CTE::IdentifierKind::Reference); } } //===---------------------------- Expressions ---------------------------===// RecursiveCoroutine emitUndefExpression(mlir::Value V) { revng_assert(isScalarType(V.getType())); C.emitLiteralIdentifier("undef"); C.emitOperator(CTE::Operator::LeftParenthesis); emitType(V.getType()); C.emitOperator(CTE::Operator::RightParenthesis); rc_return; } RecursiveCoroutine emitImmediateExpression(mlir::Value V) { auto E = V.getDefiningOp(); emitIntegerImmediate(E.getValue(), E.getResult().getType()); rc_return; } RecursiveCoroutine emitStringLiteralExpression(mlir::Value V) { auto E = V.getDefiningOp(); C.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; C.emitPunctuator(CTE::Punctuator::LeftBrace); for (auto [I, Initializer] : llvm::enumerate(E.getInitializers())) { if (I != 0) { C.emitPunctuator(CTE::Punctuator::Comma); C.emitSpace(); } rc_recur emitExpression(Initializer); } C.emitPunctuator(CTE::Punctuator::RightBrace); } RecursiveCoroutine emitAggregateExpression(mlir::Value V) { auto E = V.getDefiningOp(); C.emitOperator(CTE::Operator::LeftParenthesis); emitType(E.getResult().getType()); C.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 &ArgAttrs = Function.getArgAttrs(E.getArgNumber()); const auto GetStringAttr = [&ArgAttrs](llvm::StringRef Name) { return mlir::cast(ArgAttrs.get(Name)).getValue(); }; C.emitIdentifier(GetStringAttr("clift.name"), GetStringAttr("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(); C.emitIdentifier(getNameAttr(E), 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"); }; C.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()); C.emitOperator(E.isIndirect() ? CTE::Operator::Arrow : CTE::Operator::Dot); auto Field = E.getClassType().getFields()[E.getMemberIndex()]; C.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; C.emitOperator(CTE::Operator::LeftBracket); rc_recur emitExpression(E.getIndex()); C.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; C.emitOperator(CTE::Operator::LeftParenthesis); for (auto [I, A] : llvm::enumerate(E.getArguments())) { if (I != 0) { C.emitPunctuator(CTE::Punctuator::Comma); C.emitSpace(); } rc_recur emitExpression(A); } C.emitOperator(CTE::Operator::RightParenthesis); } static bool isHiddenCast(CastOp Cast) { return Cast.getKind() == CastKind::Decay; } static mlir::Value unwrapHiddenCasts(CastOp Cast) { revng_assert(isHiddenCast(Cast)); while (true) { auto InnerCast = Cast.getValue().getDefiningOp(); if (not InnerCast or not isHiddenCast(InnerCast)) break; } return Cast.getValue(); } RecursiveCoroutine emitCastExpression(mlir::Value V) { auto E = V.getDefiningOp(); emitCast(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) { return emitExpression(unwrapHiddenCasts(V.getDefiningOp())); } RecursiveCoroutine emitTernaryExpression(mlir::Value V) { auto E = V.getDefiningOp(); rc_recur emitExpression(E.getCondition()); C.emitSpace(); C.emitOperator(CTE::Operator::Question); C.emitSpace(); rc_recur emitExpression(E.getLhs()); C.emitSpace(); C.emitOperator(CTE::Operator::Colon); C.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); C.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())) { if (T.getKind() == PrimitiveKind::SignedKind) return 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)) C.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); C.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)) C.emitSpace(); C.emitOperator(getOperator(Op)); C.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 (mlir::isa(E)) { 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, }; } 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."); } RecursiveCoroutine emitExpression(mlir::Value V) { const ExpressionEmitInfo Info = getExpressionEmitInfo(V); bool PrintParentheses = Info.Precedence <= CurrentPrecedence and Info.Precedence != OperatorPrecedence::Primary; if (PrintParentheses) C.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; // Emit the expression using the member function returned by // getExpressionEmitInfo. rc_recur(this->*Info.Emit)(V); } if (PrintParentheses) C.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 = getNameAttr(S), .Location = S.getHandle(), .Attributes = getDeclarationOpAttributes(S), .Kind = CTE::EntityKind::LocalVariable, }); if (not S.getInitializer().empty()) { C.emitSpace(); C.emitOperator(CTE::Operator::Equals); C.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); } C.emitPunctuator(CTE::Punctuator::Semicolon); if (EmitNewline) C.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 = C.enterScope(CTE::ScopeKind::None, CTE::Delimiter::None, /*Indent=*/-1); C.emitIdentifier(getNameAttr(Label), getLocationAttr(Label), CTE::EntityKind::Label, CTE::IdentifierKind::Definition); C.emitPunctuator(CTE::Punctuator::Colon); if (RequiresEmptyExpression) { C.emitSpace(); C.emitPunctuator(CTE::Punctuator::Semicolon); } C.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()); C.emitPunctuator(CTE::Punctuator::Semicolon); C.emitNewline(); } RecursiveCoroutine emitLabeledJumpStatement(JumpStatementOpInterface S) { auto LabelOp = S.getLabel().getDefiningOp(); if (mlir::isa(S)) C.emitKeyword(CTE::Keyword::Goto); else if (mlir::isa(S)) C.emitLiteralIdentifier("break_to"); else if (mlir::isa(S)) C.emitLiteralIdentifier("continue_to"); else revng_abort("Unsupported jump statement"); C.emitSpace(); C.emitIdentifier(getNameAttr(LabelOp), getLocationAttr(LabelOp), CTE::EntityKind::Label, CTE::IdentifierKind::Reference); C.emitPunctuator(CTE::Punctuator::Semicolon); C.emitNewline(); rc_return; } RecursiveCoroutine emitReturnStatement(ReturnOp S) { C.emitKeyword(CTE::Keyword::Return); if (not S.getResult().empty()) { C.emitSpace(); rc_recur emitExpressionRegion(S.getResult()); } C.emitPunctuator(CTE::Punctuator::Semicolon); C.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) { C.emitKeyword(CTE::Keyword::If); C.emitSpace(); C.emitPunctuator(CTE::Punctuator::LeftParenthesis); rc_recur emitExpressionRegion(S.getCondition()); C.emitPunctuator(CTE::Punctuator::RightParenthesis); rc_recur emitImplicitBlockStatement(S.getThen(), EmitBlocks); if (S.getElse().empty()) break; if (EmitBlocks) C.emitSpace(); C.emitKeyword(CTE::Keyword::Else); if (auto ElseIf = getOnlyOp(S.getElse())) { S = ElseIf; C.emitSpace(); } else { rc_recur emitImplicitBlockStatement(S.getElse(), EmitBlocks); break; } } if (EmitBlocks) C.emitNewline(); } RecursiveCoroutine emitCaseRegion(mlir::Region &R) { bool Break = hasFallthrough(R); if (rc_recur emitImplicitBlockStatement(R)) { if (Break) C.emitSpace(); else C.emitNewline(); } if (Break) { C.emitKeyword(CTE::Keyword::Break); C.emitPunctuator(CTE::Punctuator::Semicolon); C.emitNewline(); } } RecursiveCoroutine emitSwitchStatement(SwitchOp S) { C.emitKeyword(CTE::Keyword::Switch); C.emitSpace(); C.emitPunctuator(CTE::Punctuator::LeftParenthesis); rc_recur emitExpressionRegion(S.getCondition()); C.emitPunctuator(CTE::Punctuator::RightParenthesis); C.emitSpace(); // Scope tags are applied within this scope: { auto Scope = C.enterScope(CTE::ScopeKind::BlockStatement, CTE::Delimiter::Braces, /*Indented=*/false); C.emitNewline(); ValueType Type = S.getConditionType(); for (unsigned I = 0, Count = S.getNumCases(); I < Count; ++I) { C.emitKeyword(CTE::Keyword::Case); C.emitSpace(); emitIntegerImmediate(S.getCaseValue(I), Type); C.emitPunctuator(CTE::Punctuator::Colon); rc_recur emitCaseRegion(S.getCaseRegion(I)); } if (S.hasDefaultCase()) { C.emitKeyword(CTE::Keyword::Default); C.emitPunctuator(CTE::Punctuator::Colon); rc_recur emitCaseRegion(S.getDefaultCaseRegion()); } } C.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) { C.emitKeyword(CTE::Keyword::For); C.emitSpace(); C.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 { C.emitPunctuator(CTE::Punctuator::Semicolon); } if (mlir::Region &R = S.getCondition(); not R.empty()) { C.emitSpace(); rc_recur emitExpressionRegion(R); } C.emitPunctuator(CTE::Punctuator::Semicolon); if (mlir::Region &R = S.getExpression(); not R.empty()) { C.emitSpace(); rc_recur emitExpressionRegion(R); } C.emitPunctuator(CTE::Punctuator::RightParenthesis); if (rc_recur emitLoopBody(S, S.getBody())) C.emitNewline(); if (auto Break = S.getBreakLabel()) emitLabelStatement(Break.getDefiningOp(), S); } RecursiveCoroutine emitWhileStatement(WhileOp S) { C.emitKeyword(CTE::Keyword::While); C.emitSpace(); C.emitPunctuator(CTE::Punctuator::LeftParenthesis); rc_recur emitExpressionRegion(S.getCondition()); C.emitPunctuator(CTE::Punctuator::RightParenthesis); if (rc_recur emitLoopBody(S, S.getBody())) C.emitNewline(); if (auto Break = S.getBreakLabel()) emitLabelStatement(Break.getDefiningOp(), S); } RecursiveCoroutine emitDoWhileStatement(DoWhileOp S) { C.emitKeyword(CTE::Keyword::Do); if (rc_recur emitLoopBody(S, S.getBody())) C.emitSpace(); C.emitKeyword(CTE::Keyword::While); C.emitSpace(); C.emitPunctuator(CTE::Punctuator::LeftParenthesis); rc_recur emitExpressionRegion(S.getCondition()); C.emitPunctuator(CTE::Punctuator::RightParenthesis); C.emitPunctuator(CTE::Punctuator::Semicolon); C.emitNewline(); if (auto Break = S.getBreakLabel()) emitLabelStatement(Break.getDefiningOp(), S); } RecursiveCoroutine emitStatement(StatementOpInterface Stmt) { mlir::Operation *Op = Stmt.getOperation(); 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 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); 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) { C.emitSpace(); ScopeKind = CTE::ScopeKind::BlockStatement; Delimiter = CTE::Delimiter::Braces; } auto Scope = C.enterScope(ScopeKind, Delimiter); C.emitNewline(); rc_recur EmitRegion(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 = C.enterScope(CTE::ScopeKind::FunctionDeclaration, CTE::Delimiter::None, /*Indented=*/false); llvm::SmallVector ParameterDeclarators; for (unsigned I = 0; I < Op.getArgCount(); ++I) { auto Attrs = Op.getArgAttrs(I); auto GetStringAttr = [&Attrs](llvm::StringRef Name) { return mlir::cast(Attrs.get(Name)).getValue(); }; mlir::ArrayAttr Attributes = {}; if (auto Attr = Attrs.get("clift.attributes")) { Attributes = mlir::cast(Attr); revng_assert(isValidAttributeArray(Attributes)); } ParameterDeclarators.emplace_back(GetStringAttr("clift.name"), GetStringAttr("clift.handle"), Attributes); } emitDeclaration(Op.getFunctionType(), DeclaratorInfo{ .Identifier = Op.getName(), .Location = Op.getHandle(), .Attributes = getDeclarationOpAttributes(Op), .Kind = CTE::EntityKind::Function, .Parameters = ParameterDeclarators, }); C.emitSpace(); auto InnerScope = C.enterScope(CTE::ScopeKind::FunctionDefinition, CTE::Delimiter::Braces); C.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. } C.emitNewline(); } }; } // namespace void clift::decompile(FunctionOp Function, CTokenEmitter &Emitter, const TargetCImplementation &Target) { CliftToCEmitter(Emitter, Target).emitFunction(Function); }