Skip to content

Commit

Permalink
Bind variables at expression compile time
Browse files Browse the repository at this point in the history
There was a map here. It's gone now.
  • Loading branch information
smurf3tte committed Dec 20, 2020
1 parent a2e0223 commit c21581a
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 95 deletions.
2 changes: 1 addition & 1 deletion Source/Core/Core/PowerPC/BreakPoints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const
if (!bp.is_temporary)
{
std::ostringstream ss;
ss << "$" << fmt::format("{:08x}", bp.address) << " ";
ss << fmt::format("${:08x} ", bp.address);
if (bp.is_enabled)
ss << "n";
if (bp.log_on_hit)
Expand Down
193 changes: 102 additions & 91 deletions Source/Core/Core/PowerPC/Expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "Core/PowerPC/Expression.h"

#include <cstdlib>
#include <optional>
#include <string>
#include <string_view>
Expand Down Expand Up @@ -99,92 +100,28 @@ static double CastFunc(expr_func* f, vec_expr_t* args, void* c)
return Common::BitCast<T>(static_cast<U>(expr_eval(&vec_nth(args, 0))));
}

static expr_func g_expr_funcs[] = {{"read_u8", HostReadFunc<u8>},
{"read_s8", HostReadFunc<s8, u8>},
{"read_u16", HostReadFunc<u16>},
{"read_s16", HostReadFunc<s16, u16>},
{"read_u32", HostReadFunc<u32>},
{"read_s32", HostReadFunc<s32, u32>},
{"read_f32", HostReadFunc<float, u32>},
{"read_f64", HostReadFunc<double, u64>},
{"write_u8", HostWriteFunc<u8>},
{"write_u16", HostWriteFunc<u16>},
{"write_u32", HostWriteFunc<u32>},
{"write_f32", HostWriteFunc<float, u32>},
{"write_f64", HostWriteFunc<double, u64>},
{"u8", CastFunc<u8>},
{"s8", CastFunc<s8, u8>},
{"u16", CastFunc<u16>},
{"s16", CastFunc<s16, u16>},
{"u32", CastFunc<u32>},
{"s32", CastFunc<s32, u32>},
{}};

const std::unordered_map<std::string_view, u32&> g_register_vars = {
{"r0", GPR(0)}, {"r1", GPR(1)}, {"r2", GPR(2)}, {"r3", GPR(3)}, {"r4", GPR(4)},
{"r5", GPR(5)}, {"r6", GPR(6)}, {"r7", GPR(7)}, {"r8", GPR(8)}, {"r9", GPR(9)},
{"r10", GPR(10)}, {"r11", GPR(11)}, {"r12", GPR(12)}, {"r13", GPR(13)}, {"r14", GPR(14)},
{"r15", GPR(15)}, {"r16", GPR(16)}, {"r17", GPR(17)}, {"r18", GPR(18)}, {"r19", GPR(19)},
{"r20", GPR(20)}, {"r21", GPR(21)}, {"r22", GPR(22)}, {"r23", GPR(23)}, {"r24", GPR(24)},
{"r25", GPR(25)}, {"r26", GPR(26)}, {"r27", GPR(27)}, {"r28", GPR(28)}, {"r29", GPR(29)},
{"r30", GPR(30)}, {"r31", GPR(31)}, {"pc", PC}, {"lr", LR}, {"ctr", CTR},
};

const std::unordered_map<std::string_view, u64&> g_float_register_vars = {
{"f0", rPS(0).ps0}, {"f1", rPS(1).ps0}, {"f2", rPS(2).ps0}, {"f3", rPS(3).ps0},
{"f4", rPS(4).ps0}, {"f5", rPS(5).ps0}, {"f6", rPS(6).ps0}, {"f7", rPS(7).ps0},
{"f8", rPS(8).ps0}, {"f9", rPS(9).ps0}, {"f10", rPS(10).ps0}, {"f11", rPS(11).ps0},
{"f12", rPS(12).ps0}, {"f13", rPS(13).ps0}, {"f14", rPS(14).ps0}, {"f15", rPS(15).ps0},
{"f16", rPS(16).ps0}, {"f17", rPS(17).ps0}, {"f18", rPS(18).ps0}, {"f19", rPS(19).ps0},
{"f20", rPS(20).ps0}, {"f21", rPS(21).ps0}, {"f22", rPS(22).ps0}, {"f23", rPS(23).ps0},
{"f24", rPS(24).ps0}, {"f25", rPS(25).ps0}, {"f26", rPS(26).ps0}, {"f27", rPS(27).ps0},
{"f28", rPS(28).ps0}, {"f29", rPS(29).ps0}, {"f30", rPS(30).ps0}, {"f31", rPS(31).ps0},
};

enum class SynchronizeDirection
{
FromRegister,
ToRegister,
};

static void SynchronizeRegisters(const expr_var_list& vars, SynchronizeDirection dir)
{
for (auto* v = vars.head; v != nullptr; v = v->next)
{
auto r = g_register_vars.find(v->name);
if (r != g_register_vars.end())
{
if (dir == SynchronizeDirection::FromRegister)
{
v->value = static_cast<double>(r->second);
}
else
{
r->second = static_cast<u32>(static_cast<s64>(v->value));
}
continue;
}

auto fr = g_float_register_vars.find(v->name);
if (fr != g_float_register_vars.end())
{
if (dir == SynchronizeDirection::FromRegister)
{
v->value = Common::BitCast<double>(fr->second);
}
else
{
fr->second = Common::BitCast<u64>(v->value);
}
continue;
}

if (dir == SynchronizeDirection::FromRegister)
{
v->value = 0; // init expression local variables to zero
}
}
}
static std::array<expr_func, 21> g_expr_funcs{{
{"read_u8", HostReadFunc<u8>},
{"read_s8", HostReadFunc<s8, u8>},
{"read_u16", HostReadFunc<u16>},
{"read_s16", HostReadFunc<s16, u16>},
{"read_u32", HostReadFunc<u32>},
{"read_s32", HostReadFunc<s32, u32>},
{"read_f32", HostReadFunc<float, u32>},
{"read_f64", HostReadFunc<double, u64>},
{"write_u8", HostWriteFunc<u8>},
{"write_u16", HostWriteFunc<u16>},
{"write_u32", HostWriteFunc<u32>},
{"write_f32", HostWriteFunc<float, u32>},
{"write_f64", HostWriteFunc<double, u64>},
{"u8", CastFunc<u8>},
{"s8", CastFunc<s8, u8>},
{"u16", CastFunc<u16>},
{"s16", CastFunc<s16, u16>},
{"u32", CastFunc<u32>},
{"s32", CastFunc<s32, u32>},
{},
}};

void ExprDeleter::operator()(expr* expression) const
{
Expand All @@ -193,19 +130,56 @@ void ExprDeleter::operator()(expr* expression) const

void ExprVarListDeleter::operator()(expr_var_list* vars) const
{
expr_destroy(nullptr, vars); // free list elements
delete vars; // free list object
// Free list elements
expr_destroy(nullptr, vars);
// Free list object
delete vars;
}

Expression::Expression(std::string_view text, ExprPointer ex, ExprVarListPointer vars)
: m_text(text), m_expr(std::move(ex)), m_vars(std::move(vars))
{
for (auto* v = m_vars->head; v != nullptr; v = v->next)
{
const std::string_view name = v->name;
VarBinding bind;

if (name.length() >= 2 && name.length() <= 3)
{
if (name[0] == 'r' || name[0] == 'f')
{
char* end = nullptr;
int index = std::strtol(name.data() + 1, &end, 10);
if (index >= 0 && index <= 31 && end == name.data() + name.length())
{
bind.type = name[0] == 'r' ? VarBindingType::GPR : VarBindingType::FPR;
bind.index = index;
}
}
else if (name == "lr")
{
bind.type = VarBindingType::SPR;
bind.index = SPR_LR;
}
else if (name == "ctr")
{
bind.type = VarBindingType::SPR;
bind.index = SPR_CTR;
}
else if (name == "pc")
{
bind.type = VarBindingType::PCtr;
}
}

m_binds.emplace_back(bind);
}
}

std::optional<Expression> Expression::TryParse(std::string_view text)
{
ExprVarListPointer vars{new expr_var_list{}};
ExprPointer ex{expr_create(text.data(), text.length(), vars.get(), g_expr_funcs)};
ExprPointer ex{expr_create(text.data(), text.length(), vars.get(), g_expr_funcs.data())};
if (!ex)
return {};

Expand All @@ -214,15 +188,52 @@ std::optional<Expression> Expression::TryParse(std::string_view text)

double Expression::Evaluate() const
{
SynchronizeRegisters(*m_vars, SynchronizeDirection::FromRegister);
SynchronizeBindings(SynchronizeDirection::From);

double result = expr_eval(m_expr.get());

SynchronizeRegisters(*m_vars, SynchronizeDirection::ToRegister);
SynchronizeBindings(SynchronizeDirection::To);

return result;
}

void Expression::SynchronizeBindings(SynchronizeDirection dir) const
{
auto bind = m_binds.begin();
for (auto* v = m_vars->head; v != nullptr; v = v->next, ++bind)
{
switch (bind->type)
{
case VarBindingType::Zero:
if (dir == SynchronizeDirection::From)
v->value = 0;
break;
case VarBindingType::GPR:
if (dir == SynchronizeDirection::From)
v->value = static_cast<double>(GPR(bind->index));
else
GPR(bind->index) = static_cast<u32>(static_cast<s64>(v->value));
break;
case VarBindingType::FPR:
if (dir == SynchronizeDirection::From)
v->value = rPS(bind->index).PS0AsDouble();
else
rPS(bind->index).SetPS0(v->value);
break;
case VarBindingType::SPR:
if (dir == SynchronizeDirection::From)
v->value = static_cast<double>(rSPR(bind->index));
else
rSPR(bind->index) = static_cast<u32>(static_cast<s64>(v->value));
break;
case VarBindingType::PCtr:
if (dir == SynchronizeDirection::From)
v->value = static_cast<double>(PC);
break;
}
}
}

std::string Expression::GetText() const
{
return m_text;
Expand Down
25 changes: 25 additions & 0 deletions Source/Core/Core/PowerPC/Expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <optional>
#include <string>
#include <string_view>
#include <vector>

struct expr;
struct expr_var_list;
Expand Down Expand Up @@ -36,11 +37,35 @@ class Expression
std::string GetText() const;

private:
enum class SynchronizeDirection
{
From,
To,
};

enum class VarBindingType
{
Zero,
GPR,
FPR,
SPR,
PCtr,
};

struct VarBinding
{
VarBindingType type = VarBindingType::Zero;
int index = -1;
};

Expression(std::string_view text, ExprPointer ex, ExprVarListPointer vars);

void SynchronizeBindings(SynchronizeDirection dir) const;

std::string m_text;
ExprPointer m_expr;
ExprVarListPointer m_vars;
std::vector<VarBinding> m_binds;
};

inline bool EvaluateCondition(const std::optional<Expression>& condition)
Expand Down
5 changes: 3 additions & 2 deletions Source/Core/DolphinQt/Debugger/BreakpointWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,9 @@ void BreakpointWidget::AddBP(u32 addr)
void BreakpointWidget::AddBP(u32 addr, bool temp, bool break_on_hit, bool log_on_hit,
const QString& condition)
{
PowerPC::breakpoints.Add(addr, temp, break_on_hit, log_on_hit,
Expression::TryParse(condition.toUtf8().constData()));
PowerPC::breakpoints.Add(
addr, temp, break_on_hit, log_on_hit,
!condition.isEmpty() ? Expression::TryParse(condition.toUtf8().constData()) : std::nullopt);

Update();
}
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/DolphinQt/Debugger/NewBreakpointDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ void NewBreakpointDialog::accept()
return;
}

QString condition = m_instruction_condition->text().trimmed();
const QString condition = m_instruction_condition->text().trimmed();

if (!condition.isEmpty() && !Expression::TryParse(condition.toUtf8().constData()))
{
Expand Down

0 comments on commit c21581a

Please sign in to comment.