Skip to content

Commit

Permalink
[LLDB] Add basic floating point ops to IR interpreter
Browse files Browse the repository at this point in the history
Patch adds support for fadd, fsub, fdiv, fmul and fcmp to IR interpreter.

~~~

OS Laboratory. Huawei RRI. Saint-Petersburg

Reviewed By: clayborg

Differential Revision: https://reviews.llvm.org/D126359
  • Loading branch information
Pavel Kosov committed Aug 10, 2022
1 parent c122658 commit f63e2cf
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 13 deletions.
88 changes: 75 additions & 13 deletions lldb/source/Expression/IRInterpreter.cpp
Expand Up @@ -167,6 +167,18 @@ class InterpreterStackFrame {
const Constant *constant = dyn_cast<Constant>(value);

if (constant) {
if (constant->getValueID() == Value::ConstantFPVal) {
if (auto *cfp = dyn_cast<ConstantFP>(constant)) {
if (cfp->getType()->isDoubleTy())
scalar = cfp->getValueAPF().convertToDouble();
else if (cfp->getType()->isFloatTy())
scalar = cfp->getValueAPF().convertToFloat();
else
return false;
return true;
}
return false;
}
APInt value_apint;

if (!ResolveConstantValue(value_apint, constant))
Expand All @@ -189,9 +201,18 @@ class InterpreterStackFrame {

lldb::offset_t offset = 0;
if (value_size <= 8) {
uint64_t u64value = value_extractor.GetMaxU64(&offset, value_size);
return AssignToMatchType(scalar, llvm::APInt(64, u64value),
value->getType());
Type *ty = value->getType();
if (ty->isDoubleTy()) {
scalar = value_extractor.GetDouble(&offset);
return true;
} else if (ty->isFloatTy()) {
scalar = value_extractor.GetFloat(&offset);
return true;
} else {
uint64_t u64value = value_extractor.GetMaxU64(&offset, value_size);
return AssignToMatchType(scalar, llvm::APInt(64, u64value),
value->getType());
}
}

return false;
Expand All @@ -205,11 +226,15 @@ class InterpreterStackFrame {
return false;

lldb_private::Scalar cast_scalar;

scalar.MakeUnsigned();
if (!AssignToMatchType(cast_scalar, scalar.UInt128(llvm::APInt()),
value->getType()))
return false;
Type *vty = value->getType();
if (vty->isFloatTy() || vty->isDoubleTy()) {
cast_scalar = scalar;
} else {
scalar.MakeUnsigned();
if (!AssignToMatchType(cast_scalar, scalar.UInt128(llvm::APInt()),
value->getType()))
return false;
}

size_t value_byte_size = m_target_data.getTypeStoreSize(value->getType());

Expand Down Expand Up @@ -543,16 +568,17 @@ bool IRInterpreter::CanInterpret(llvm::Module &module, llvm::Function &function,
} break;
case Instruction::GetElementPtr:
break;
case Instruction::FCmp:
case Instruction::ICmp: {
ICmpInst *icmp_inst = dyn_cast<ICmpInst>(&ii);
CmpInst *cmp_inst = dyn_cast<CmpInst>(&ii);

if (!icmp_inst) {
if (!cmp_inst) {
error.SetErrorToGenericError();
error.SetErrorString(interpreter_internal_error);
return false;
}

switch (icmp_inst->getPredicate()) {
switch (cmp_inst->getPredicate()) {
default: {
LLDB_LOGF(log, "Unsupported ICmp predicate: %s",
PrintValue(&ii).c_str());
Expand All @@ -561,11 +587,17 @@ bool IRInterpreter::CanInterpret(llvm::Module &module, llvm::Function &function,
error.SetErrorString(unsupported_opcode_error);
return false;
}
case CmpInst::FCMP_OEQ:
case CmpInst::ICMP_EQ:
case CmpInst::FCMP_UNE:
case CmpInst::ICMP_NE:
case CmpInst::FCMP_OGT:
case CmpInst::ICMP_UGT:
case CmpInst::FCMP_OGE:
case CmpInst::ICMP_UGE:
case CmpInst::FCMP_OLT:
case CmpInst::ICMP_ULT:
case CmpInst::FCMP_OLE:
case CmpInst::ICMP_ULE:
case CmpInst::ICMP_SGT:
case CmpInst::ICMP_SGE:
Expand Down Expand Up @@ -595,6 +627,11 @@ bool IRInterpreter::CanInterpret(llvm::Module &module, llvm::Function &function,
case Instruction::Xor:
case Instruction::ZExt:
break;
case Instruction::FAdd:
case Instruction::FSub:
case Instruction::FMul:
case Instruction::FDiv:
break;
}

for (unsigned oi = 0, oe = ii.getNumOperands(); oi != oe; ++oi) {
Expand Down Expand Up @@ -709,7 +746,11 @@ bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function,
case Instruction::AShr:
case Instruction::And:
case Instruction::Or:
case Instruction::Xor: {
case Instruction::Xor:
case Instruction::FAdd:
case Instruction::FSub:
case Instruction::FMul:
case Instruction::FDiv: {
const BinaryOperator *bin_op = dyn_cast<BinaryOperator>(inst);

if (!bin_op) {
Expand Down Expand Up @@ -748,12 +789,15 @@ bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function,
default:
break;
case Instruction::Add:
case Instruction::FAdd:
result = L + R;
break;
case Instruction::Mul:
case Instruction::FMul:
result = L * R;
break;
case Instruction::Sub:
case Instruction::FSub:
result = L - R;
break;
case Instruction::SDiv:
Expand All @@ -766,6 +810,9 @@ bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function,
R.MakeUnsigned();
result = L / R;
break;
case Instruction::FDiv:
result = L / R;
break;
case Instruction::SRem:
L.MakeSigned();
R.MakeSigned();
Expand Down Expand Up @@ -1028,8 +1075,9 @@ bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function,
LLDB_LOGF(log, " Poffset : %s", frame.SummarizeValue(inst).c_str());
}
} break;
case Instruction::FCmp:
case Instruction::ICmp: {
const ICmpInst *icmp_inst = cast<ICmpInst>(inst);
const CmpInst *icmp_inst = cast<CmpInst>(inst);

CmpInst::Predicate predicate = icmp_inst->getPredicate();

Expand Down Expand Up @@ -1059,9 +1107,11 @@ bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function,
default:
return false;
case CmpInst::ICMP_EQ:
case CmpInst::FCMP_OEQ:
result = (L == R);
break;
case CmpInst::ICMP_NE:
case CmpInst::FCMP_UNE:
result = (L != R);
break;
case CmpInst::ICMP_UGT:
Expand All @@ -1074,16 +1124,28 @@ bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function,
R.MakeUnsigned();
result = (L >= R);
break;
case CmpInst::FCMP_OGE:
result = (L >= R);
break;
case CmpInst::FCMP_OGT:
result = (L > R);
break;
case CmpInst::ICMP_ULT:
L.MakeUnsigned();
R.MakeUnsigned();
result = (L < R);
break;
case CmpInst::FCMP_OLT:
result = (L < R);
break;
case CmpInst::ICMP_ULE:
L.MakeUnsigned();
R.MakeUnsigned();
result = (L <= R);
break;
case CmpInst::FCMP_OLE:
result = (L <= R);
break;
case CmpInst::ICMP_SGT:
L.MakeSigned();
R.MakeSigned();
Expand Down
3 changes: 3 additions & 0 deletions lldb/test/API/lang/c/fpeval/Makefile
@@ -0,0 +1,3 @@
C_SOURCES := main.c

include Makefile.rules
101 changes: 101 additions & 0 deletions lldb/test/API/lang/c/fpeval/TestFPEval.py
@@ -0,0 +1,101 @@
"""Tests IR interpreter handling of basic floating point operations (fadd, fsub, etc)."""

import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil

class FPEvalTestCase(TestBase):

mydir = TestBase.compute_mydir(__file__)

def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
self.jit_opts = lldb.SBExpressionOptions()
self.jit_opts.SetAllowJIT(True)
self.no_jit_opts = lldb.SBExpressionOptions()
self.no_jit_opts.SetAllowJIT(False)
# Find the line number to break inside main().
self.line = line_number('main.c', '// Set break point at this line.')

def test(self):
"""Test floating point expressions while jitter is disabled."""
self.build()
exe = self.getBuildArtifact("a.out")
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)

# Break inside the main.
lldbutil.run_break_set_by_file_and_line(
self, "main.c", self.line, num_expected_locations=1, loc_exact=True)


value = self.frame().EvaluateExpression("a + b", self.no_jit_opts)

self.runCmd("run", RUN_SUCCEEDED)
# test double
self.expect("expr --allow-jit false -- a + b", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['double', '44'])
self.expect("expr --allow-jit false -- a - b", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['double', '40'])
self.expect("expr --allow-jit false -- a / b", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['double', '21'])
self.expect("expr --allow-jit false -- a * b", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['double', '84'])
self.expect("expr --allow-jit false -- a + 2", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['double', '44'])
self.expect("expr --allow-jit false -- a > b", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['true'])
self.expect("expr --allow-jit false -- a >= b", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['true'])
self.expect("expr --allow-jit false -- a < b", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['false'])
self.expect("expr --allow-jit false -- a <= b", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['false'])
self.expect("expr --allow-jit false -- a == b", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['false'])
self.expect("expr --allow-jit false -- a != b", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['true'])

# test single
self.expect("expr --allow-jit false -- f + q", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['float', '44'])
self.expect("expr --allow-jit false -- f - q", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['float', '40'])
self.expect("expr --allow-jit false -- f / q", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['float', '21'])
self.expect("expr --allow-jit false -- f * q", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['float', '84'])
self.expect("expr --allow-jit false -- f + 2", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['float', '44'])
self.expect("expr --allow-jit false -- f > q", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['true'])
self.expect("expr --allow-jit false -- f >= q", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['true'])
self.expect("expr --allow-jit false -- f < q", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['false'])
self.expect("expr --allow-jit false -- f <= q", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['false'])
self.expect("expr --allow-jit false -- f == q", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['false'])
self.expect("expr --allow-jit false -- f != q", VARIABLES_DISPLAYED_CORRECTLY,
substrs=['true'])

# compare jit and interpreter output
self.assertTrue(self.process().IsValid())
thread = lldbutil.get_stopped_thread(self.process(), lldb.eStopReasonBreakpoint)
self.assertTrue(thread.IsValid())
frame = thread.GetSelectedFrame()
self.assertTrue(frame.IsValid())

dividents = [42, 79, 666]
divisors = [1.618, 2.718281828, 3.1415926535, 6.62607015]

for x in dividents:
for y in divisors:
vardef = "double x = {0}, y = {1};".format(x, y)
v1 = frame.EvaluateExpression("{0}; eval(x, y, 2)".format(vardef), self.jit_opts)
v2 = frame.EvaluateExpression("{0}; x / y".format(vardef), self.no_jit_opts)
self.assertTrue(v1.IsValid() and v2.IsValid())
self.assertTrue(str(v1.GetData()) == str(v2.GetData()))

16 changes: 16 additions & 0 deletions lldb/test/API/lang/c/fpeval/main.c
@@ -0,0 +1,16 @@
double eval(double a, double b, int op) {
switch (op) {
case 0: return a+b;
case 1: return a-b;
case 2: return a/b;
case 3: return a*b;
default: return 0;
}
}

int main (int argc, char const *argv[])
{
double a = 42.0, b = 2.0;
float f = 42.0, q = 2.0;
return 0; //// Set break point at this line.
}

0 comments on commit f63e2cf

Please sign in to comment.