Skip to content

Commit bfc4d39

Browse files
decomp: Support LZCOUNT & POPCOUNT ops (#118)
* decomp: Add builtin call helper - Supports adding arbitrary builtins, fixing an issue with trying to add LZCOUNT & POPCOUNT support * decomp: Support LZCOUNT & POPCOUNT - Relies on emission of clang's builtin functions for the pcode op
1 parent 1286d5d commit bfc4d39

4 files changed

Lines changed: 81 additions & 2 deletions

File tree

include/patchestry/AST/OperationBuilder.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,12 @@ namespace patchestry::ast {
156156
clang::ASTContext &ctx, const Function &function, const Operation &op
157157
);
158158

159+
std::pair< clang::Stmt *, bool >
160+
create_popcount(clang::ASTContext &ctx, const Function &function, const Operation &op);
161+
162+
std::pair< clang::Stmt *, bool >
163+
create_lzcount(clang::ASTContext &ctx, const Function &function, const Operation &op);
164+
159165
std::pair< clang::Stmt *, bool > create_builtin_call_expr(
160166
clang::ASTContext &ctx, const Function &function, const Operation &op,
161167
clang::Builtin::ID id

include/patchestry/Ghidra/Pcode.def

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,6 @@
7676
X(DECLARE_PARAMETER) \
7777
X(DECLARE_LOCAL) \
7878
X(DECLARE_TEMPORARY) \
79-
X(ADDRESS_OF)
79+
X(ADDRESS_OF) \
80+
X(POPCOUNT) \
81+
X(LZCOUNT)

lib/patchestry/AST/FunctionBuilder.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,10 @@ namespace patchestry::ast {
643643
return op_builder->create_declare_local(ctx, op);
644644
case Mnemonic::OP_ADDRESS_OF:
645645
return op_builder->create_unary_operation(ctx, function, op, clang::UO_AddrOf);
646+
case Mnemonic::OP_POPCOUNT:
647+
return op_builder->create_popcount(ctx, function, op);
648+
case Mnemonic::OP_LZCOUNT:
649+
return op_builder->create_lzcount(ctx, function, op);
646650
case Mnemonic::OP_UNKNOWN:
647651
assert(false);
648652
break;

lib/patchestry/AST/OperationStmt.cpp

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <clang/Basic/LangOptions.h>
2323
#include <clang/Basic/SourceLocation.h>
2424
#include <clang/Basic/Specifiers.h>
25+
#include <clang/Sema/Lookup.h>
2526
#include <clang/Sema/Sema.h>
2627
#include <llvm/ADT/APInt.h>
2728
#include <llvm/Support/Casting.h>
@@ -89,6 +90,42 @@ namespace patchestry::ast {
8990
}
9091
}
9192

93+
/**
94+
* Helper function to create a builtin call expression.
95+
*/
96+
clang::Expr *create_builtin_call(
97+
const clang::ASTContext &ctx, clang::Sema &sema,
98+
const clang::Builtin::ID builtin_id, std::vector< clang::Expr * > &args,
99+
const clang::SourceLocation loc
100+
) {
101+
// Get the builtin name and create a lookup result
102+
const auto name = ctx.BuiltinInfo.getName(builtin_id);
103+
const clang::LookupResult r(
104+
sema, &ctx.Idents.get(name), loc, clang::Sema::LookupOrdinaryName
105+
);
106+
auto *II = r.getLookupName().getAsIdentifierInfo();
107+
108+
// Get the builtin type
109+
clang::ASTContext::GetBuiltinTypeError error{};
110+
const auto ty = ctx.GetBuiltinType(builtin_id, error);
111+
assert(!error && "Failed to get builtin type");
112+
113+
// Create the builtin declaration
114+
auto *builtin_decl = sema.CreateBuiltin(II, ty, builtin_id, loc);
115+
116+
// Create a DeclRefExpr for the builtin
117+
auto *decl_ref = clang::DeclRefExpr::Create(
118+
ctx, clang::NestedNameSpecifierLoc(), clang::SourceLocation(), builtin_decl,
119+
false, loc, builtin_decl->getType(), clang::VK_PRValue
120+
);
121+
122+
// Build the call expression
123+
auto result = sema.BuildCallExpr(nullptr, decl_ref, loc, args, loc);
124+
assert(!result.isInvalid() && "Failed to build builtin call expr");
125+
126+
return result.getAs< clang::Expr >();
127+
}
128+
92129
} // namespace
93130

94131
/**
@@ -1215,6 +1252,34 @@ namespace patchestry::ast {
12151252
return create_builtin_call_expr(ctx, function, op, clang::Builtin::BIroundf);
12161253
}
12171254

1255+
std::pair< clang::Stmt *, bool > OpBuilder::create_popcount(
1256+
clang::ASTContext &ctx, const Function &function, const Operation &op
1257+
) {
1258+
if (op.inputs.size() != 1U || op.mnemonic != Mnemonic::OP_POPCOUNT) {
1259+
LOG(ERROR) << "POPCOUNT operation is invalid or has invalid input operand. key: "
1260+
<< op.key << "\n";
1261+
return {};
1262+
}
1263+
1264+
// Use __builtin_popcount for population count
1265+
return create_builtin_call_expr(
1266+
ctx, function, op, clang::Builtin::BI__builtin_popcount
1267+
);
1268+
}
1269+
1270+
std::pair< clang::Stmt *, bool > OpBuilder::create_lzcount(
1271+
clang::ASTContext &ctx, const Function &function, const Operation &op
1272+
) {
1273+
if (op.inputs.size() != 1U || op.mnemonic != Mnemonic::OP_LZCOUNT) {
1274+
LOG(ERROR) << "LZCOUNT operation is invalid or has invalid input operand. key: "
1275+
<< op.key << "\n";
1276+
return {};
1277+
}
1278+
1279+
// Use __builtin_clz for count leading zeros
1280+
return create_builtin_call_expr(ctx, function, op, clang::Builtin::BI__builtin_clz);
1281+
}
1282+
12181283
std::pair< clang::Stmt *, bool > OpBuilder::create_builtin_call_expr(
12191284
clang::ASTContext &ctx, const Function &function, const Operation &op,
12201285
clang::Builtin::ID id
@@ -1224,7 +1289,9 @@ namespace patchestry::ast {
12241289
auto *input_expr =
12251290
clang::dyn_cast< clang::Expr >(create_varnode(ctx, function, op.inputs[0]));
12261291

1227-
auto *call_expr = sema().BuildBuiltinCallExpr(op_loc, id, { input_expr });
1292+
std::vector args = { input_expr };
1293+
auto *call_expr = create_builtin_call(ctx, sema(), id, args, op_loc);
1294+
12281295
if (op.output.has_value()) {
12291296
return { call_expr, true };
12301297
}

0 commit comments

Comments
 (0)