[Lldb-commits] [lldb] [lldb] Add FP conversion instructions to IR interpreter (PR #175292)
Igor Kudrin via lldb-commits
lldb-commits at lists.llvm.org
Wed Jan 28 16:52:19 PST 2026
https://github.com/igorkudrin updated https://github.com/llvm/llvm-project/pull/175292
>From ba9318db283858884e395ec2329ef2f8891370b4 Mon Sep 17 00:00:00 2001
From: Igor Kudrin <ikudrin at accesssoftek.com>
Date: Thu, 8 Jan 2026 22:53:38 -0800
Subject: [PATCH 1/7] [lldb] Add FP conversion instructions to IR interpreter
This allows expressions that use these conversions to be executed when
JIT is not available.
---
lldb/source/Expression/IRInterpreter.cpp | 73 +++++++++++++++++++
.../ir-interpreter/TestIRInterpreter.py | 70 ++++++++++++++++++
2 files changed, 143 insertions(+)
diff --git a/lldb/source/Expression/IRInterpreter.cpp b/lldb/source/Expression/IRInterpreter.cpp
index 91404831aeb9b..3c43646f51eff 100644
--- a/lldb/source/Expression/IRInterpreter.cpp
+++ b/lldb/source/Expression/IRInterpreter.cpp
@@ -611,6 +611,8 @@ bool IRInterpreter::CanInterpret(llvm::Module &module, llvm::Function &function,
} break;
case Instruction::And:
case Instruction::AShr:
+ case Instruction::FPToUI:
+ case Instruction::FPToSI:
case Instruction::IntToPtr:
case Instruction::PtrToInt:
case Instruction::Load:
@@ -635,6 +637,18 @@ bool IRInterpreter::CanInterpret(llvm::Module &module, llvm::Function &function,
case Instruction::FMul:
case Instruction::FDiv:
break;
+ case Instruction::UIToFP:
+ case Instruction::SIToFP:
+ case Instruction::FPTrunc:
+ case Instruction::FPExt:
+ if (!ii.getType()->isFloatTy() && !ii.getType()->isDoubleTy()) {
+ LLDB_LOGF(log, "Unsupported instruction: %s",
+ PrintValue(&ii).c_str());
+ error =
+ lldb_private::Status::FromErrorString(unsupported_opcode_error);
+ return false;
+ }
+ break;
}
for (unsigned oi = 0, oe = ii.getNumOperands(); oi != oe; ++oi) {
@@ -1256,6 +1270,65 @@ bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function,
LLDB_LOGF(log, " = : %s", frame.SummarizeValue(inst).c_str());
}
} break;
+ case Instruction::FPToUI:
+ case Instruction::FPToSI: {
+ Value *src_operand = inst->getOperand(0);
+
+ lldb_private::Scalar S;
+ if (!frame.EvaluateValue(S, src_operand, module)) {
+ LLDB_LOGF(log, "Couldn't evaluate %s", PrintValue(src_operand).c_str());
+ error = lldb_private::Status::FromErrorString(bad_value_error);
+ return false;
+ }
+
+ assert(inst->getType()->isIntegerTy() && "Unexpected target type");
+ llvm::APSInt result(inst->getType()->getIntegerBitWidth(),
+ /*isUnsigned=*/inst->getOpcode() ==
+ Instruction::FPToUI);
+ assert(S.GetType() == lldb_private::Scalar::e_float &&
+ "Unexpected source type");
+ bool isExact;
+ S.GetAPFloat().convertToInteger(result, llvm::APFloat::rmTowardZero,
+ &isExact);
+ lldb_private::Scalar R(result);
+
+ frame.AssignValue(inst, R, module);
+ if (log) {
+ LLDB_LOGF(log, "Interpreted a %s", inst->getOpcodeName());
+ LLDB_LOGF(log, " Src : %s", frame.SummarizeValue(src_operand).c_str());
+ LLDB_LOGF(log, " = : %s", frame.SummarizeValue(inst).c_str());
+ }
+ } break;
+ case Instruction::UIToFP:
+ case Instruction::SIToFP:
+ case Instruction::FPTrunc:
+ case Instruction::FPExt: {
+ Value *src_operand = inst->getOperand(0);
+
+ lldb_private::Scalar S;
+ if (!frame.EvaluateValue(S, src_operand, module)) {
+ LLDB_LOGF(log, "Couldn't evaluate %s", PrintValue(src_operand).c_str());
+ error = lldb_private::Status::FromErrorString(bad_value_error);
+ return false;
+ }
+ lldb_private::Scalar R;
+
+ Type *result_type = inst->getType();
+ assert(
+ (result_type->isFloatTy() || result_type->isDoubleTy()) &&
+ "Unsupported result type; CanInterpret() should have checked that");
+ if (result_type->isFloatTy())
+ R = S.Float();
+ else
+ R = S.Double();
+
+ frame.AssignValue(inst, R, module);
+ if (log) {
+ LLDB_LOGF(log, "Interpreted a %s", inst->getOpcodeName());
+ LLDB_LOGF(log, " Src : %s", frame.SummarizeValue(src_operand).c_str());
+ LLDB_LOGF(log, " = : %s", frame.SummarizeValue(inst).c_str());
+ }
+ } break;
case Instruction::Load: {
const LoadInst *load_inst = cast<LoadInst>(inst);
diff --git a/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py b/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
index 23188ef898d56..26be9ab952db7 100644
--- a/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
+++ b/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
@@ -172,3 +172,73 @@ def test_type_conversions(self):
self.assertEqual(short_val.GetValueAsSigned(), -1)
long_val = target.EvaluateExpression("(long) " + short_val.GetName())
self.assertEqual(long_val.GetValueAsSigned(), -1)
+
+ def test_fpconv(self):
+ self.build_and_run()
+
+ interp_options = lldb.SBExpressionOptions()
+ interp_options.SetLanguage(lldb.eLanguageTypeC_plus_plus)
+ interp_options.SetAllowJIT(False)
+
+ jit_options = lldb.SBExpressionOptions()
+ jit_options.SetLanguage(lldb.eLanguageTypeC_plus_plus)
+ jit_options.SetAllowJIT(True)
+
+ set_up_expressions = [
+ "int $i = 3",
+ "int $n = -3",
+ "unsigned $u = 5",
+ "long $l = -7",
+ "float $f = 9.0625",
+ "double $d = 13.75",
+ "float $nf = -11.25",
+ ]
+
+ expressions = [
+ "$i + $f",
+ "$d - $n",
+ "$u + $f",
+ "$u + $d",
+ "(int)$d",
+ "(int)$f",
+ "(long)$d",
+ "(short)$f",
+ "(long)$nf",
+ "(unsigned short)$f",
+ "(unsigned)$d",
+ "(unsigned long)$d",
+ "(float)$d",
+ "(double)$f",
+ "(double)$nf",
+ ]
+
+ for expression in set_up_expressions:
+ self.frame().EvaluateExpression(expression, interp_options)
+
+ func_call = "(int)getpid()"
+ if lldbplatformutil.getPlatform() == "windows":
+ func_call = "(int)GetCurrentProcessId()"
+
+ for expression in expressions:
+ interp_expression = expression
+ jit_expression = func_call + "; " + expression
+
+ interp_result = (
+ self.frame()
+ .EvaluateExpression(interp_expression, interp_options)
+ )
+ jit_result = (
+ self.frame()
+ .EvaluateExpression(jit_expression, jit_options)
+ )
+
+ self.assertEqual(
+ interp_result.GetValue(),
+ jit_result.GetValue(),
+ "Values match for " + expression,
+ )
+ self.assertEqual(
+ interp_result.GetTypeName(),
+ jit_result.GetTypeName(),
+ "Types match for " + expression,
+ )
>From 61d9e3cf6757f853dabb1a7c6a72bb7b656ce325 Mon Sep 17 00:00:00 2001
From: Igor Kudrin <ikudrin at accesssoftek.com>
Date: Fri, 9 Jan 2026 22:43:53 -0800
Subject: [PATCH 2/7] fixup! formatting
---
.../expression/ir-interpreter/TestIRInterpreter.py | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py b/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
index 26be9ab952db7..3ce15d801818c 100644
--- a/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
+++ b/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
@@ -223,14 +223,10 @@ def test_fpconv(self):
interp_expression = expression
jit_expression = func_call + "; " + expression
- interp_result = (
- self.frame()
- .EvaluateExpression(interp_expression, interp_options)
- )
- jit_result = (
- self.frame()
- .EvaluateExpression(jit_expression, jit_options)
+ interp_result = self.frame().EvaluateExpression(
+ interp_expression, interp_options
)
+ jit_result = self.frame().EvaluateExpression(jit_expression, jit_options)
self.assertEqual(
interp_result.GetValue(),
>From a3df8184cd2f364a463a0301ba64e61f897f542d Mon Sep 17 00:00:00 2001
From: Igor Kudrin <ikudrin at accesssoftek.com>
Date: Mon, 12 Jan 2026 18:26:59 -0800
Subject: [PATCH 3/7] fixup! add comments to test expressions
---
.../ir-interpreter/TestIRInterpreter.py | 30 +++++++++----------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py b/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
index 3ce15d801818c..2163d045ef872 100644
--- a/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
+++ b/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
@@ -195,21 +195,21 @@ def test_fpconv(self):
]
expressions = [
- "$i + $f",
- "$d - $n",
- "$u + $f",
- "$u + $d",
- "(int)$d",
- "(int)$f",
- "(long)$d",
- "(short)$f",
- "(long)$nf",
- "(unsigned short)$f",
- "(unsigned)$d",
- "(unsigned long)$d",
- "(float)$d",
- "(double)$f",
- "(double)$nf",
+ "$i + $f", # sitofp i32 to float
+ "$d - $n", # sitofp i32 to double
+ "$u + $f", # uitofp i32 to float
+ "$u + $d", # uitofp i32 to double
+ "(int)$d", # fptosi double to i32
+ "(int)$f", # fptosi float to i32
+ "(long)$d", # fptosi double to i64
+ "(short)$f", # fptosi float to i16
+ "(long)$nf", # fptosi float to i64
+ "(unsigned short)$f", # fptoui float to i16
+ "(unsigned)$d", # fptoui double to i32
+ "(unsigned long)$d", # fptoui double to i64
+ "(float)$d", # fptrunc double to float
+ "(double)$f", # fpext float to double
+ "(double)$nf", # fpext float to double
]
for expression in set_up_expressions:
>From 8480377627a33ebfa671e8a5be411eb8dcca7d00 Mon Sep 17 00:00:00 2001
From: Igor Kudrin <ikudrin at accesssoftek.com>
Date: Mon, 12 Jan 2026 18:34:40 -0800
Subject: [PATCH 4/7] fixup! reject invalid conversions
---
lldb/source/Expression/IRInterpreter.cpp | 21 +++++++++++--
.../ir-interpreter/TestIRInterpreter.py | 31 +++++++++++++++++++
2 files changed, 50 insertions(+), 2 deletions(-)
diff --git a/lldb/source/Expression/IRInterpreter.cpp b/lldb/source/Expression/IRInterpreter.cpp
index 3c43646f51eff..f6365ac948f37 100644
--- a/lldb/source/Expression/IRInterpreter.cpp
+++ b/lldb/source/Expression/IRInterpreter.cpp
@@ -70,6 +70,13 @@ static std::string PrintType(const Type *type, bool truncate = false) {
return s;
}
+static std::string PrintScalar(const lldb_private::Scalar &value) {
+ std::string s;
+ raw_string_ostream rso(s);
+ rso << value;
+ return s;
+}
+
static bool CanIgnoreCall(const CallInst *call) {
const llvm::Function *called_function = call->getCalledFunction();
@@ -477,6 +484,8 @@ static const char *timeout_error =
"Reached timeout while interpreting expression";
static const char *too_many_functions_error =
"Interpreter doesn't handle modules with multiple function bodies.";
+static const char *bad_conversion_error =
+ "Interpreter couldn't convert a value";
static bool CanResolveConstant(llvm::Constant *constant) {
switch (constant->getValueID()) {
@@ -1288,8 +1297,16 @@ bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function,
assert(S.GetType() == lldb_private::Scalar::e_float &&
"Unexpected source type");
bool isExact;
- S.GetAPFloat().convertToInteger(result, llvm::APFloat::rmTowardZero,
- &isExact);
+ llvm::APFloatBase::opStatus status = S.GetAPFloat().convertToInteger(
+ result, llvm::APFloat::rmTowardZero, &isExact);
+ // Casting floating point values that are out of bounds of the target type
+ // is undefined behaviour.
+ if (status & llvm::APFloatBase::opInvalidOp) {
+ LLDB_LOGF(log, "Couldn't convert %s to %s", PrintScalar(S).c_str(),
+ PrintType(inst->getType()).c_str());
+ error = lldb_private::Status::FromErrorString(bad_conversion_error);
+ return false;
+ }
lldb_private::Scalar R(result);
frame.AssignValue(inst, R, module);
diff --git a/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py b/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
index 2163d045ef872..e86089178d50b 100644
--- a/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
+++ b/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
@@ -238,3 +238,34 @@ def test_fpconv(self):
jit_result.GetTypeName(),
"Types match for " + expression,
)
+
+ def test_fpconv_ub(self):
+ target = self.dbg.GetDummyTarget()
+
+ set_up_expressions = [
+ "float $f = 3e9",
+ "double $d = 1e20",
+ "float $nf = -1.5",
+ ]
+
+ expressions = [
+ "(int)$f",
+ "(long)$d",
+ "(unsigned)$nf",
+ ]
+
+ for expression in set_up_expressions:
+ target.EvaluateExpression(expression)
+
+ # The IR Interpreter returns an error if a value cannot be converted.
+ for expression in expressions:
+ result = target.EvaluateExpression(expression)
+ self.assertIn(
+ "Interpreter couldn't convert a value", str(result.GetError())
+ )
+
+ # The conversion should succeed if the destination type can represent the result.
+ self.expect_expr(
+ "(unsigned)$f", result_type="unsigned int", result_value="3000000000"
+ )
+ self.expect_expr("(int)$nf", result_type="int", result_value="-1")
>From a8911df34f64d12e57921916a22ef53a28c249a6 Mon Sep 17 00:00:00 2001
From: Igor Kudrin <ikudrin at accesssoftek.com>
Date: Mon, 19 Jan 2026 15:21:49 -0800
Subject: [PATCH 5/7] fixup! add a comment
---
.../API/commands/expression/ir-interpreter/TestIRInterpreter.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py b/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
index e86089178d50b..206c62cf574c7 100644
--- a/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
+++ b/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
@@ -221,6 +221,7 @@ def test_fpconv(self):
for expression in expressions:
interp_expression = expression
+ # Calling a function forces the expression to be executed with JIT.
jit_expression = func_call + "; " + expression
interp_result = self.frame().EvaluateExpression(
>From d82a5a7bb72e177954bf1e221283d377581bc520 Mon Sep 17 00:00:00 2001
From: Igor Kudrin <ikudrin at accesssoftek.com>
Date: Tue, 27 Jan 2026 23:10:30 -0800
Subject: [PATCH 6/7] fixup! LLDB_LOGF -> LLDB_LOG, remove PrintScalar()
---
lldb/source/Expression/IRInterpreter.cpp | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/lldb/source/Expression/IRInterpreter.cpp b/lldb/source/Expression/IRInterpreter.cpp
index 437513f0526a4..6dd7253eb2f1b 100644
--- a/lldb/source/Expression/IRInterpreter.cpp
+++ b/lldb/source/Expression/IRInterpreter.cpp
@@ -70,13 +70,6 @@ static std::string PrintType(const Type *type, bool truncate = false) {
return s;
}
-static std::string PrintScalar(const lldb_private::Scalar &value) {
- std::string s;
- raw_string_ostream rso(s);
- rso << value;
- return s;
-}
-
static bool CanIgnoreCall(const CallInst *call) {
const llvm::Function *called_function = call->getCalledFunction();
@@ -1303,8 +1296,7 @@ bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function,
// Casting floating point values that are out of bounds of the target type
// is undefined behaviour.
if (status & llvm::APFloatBase::opInvalidOp) {
- LLDB_LOGF(log, "Couldn't convert %s to %s", PrintScalar(S).c_str(),
- PrintType(inst->getType()).c_str());
+ LLDB_LOG(log, "Couldn't convert {0} to {1}", S, *inst->getType());
error = lldb_private::Status::FromErrorString(bad_conversion_error);
return false;
}
>From 0d94d987780c1de296d429287017597beae99957 Mon Sep 17 00:00:00 2001
From: Igor Kudrin <ikudrin at accesssoftek.com>
Date: Wed, 28 Jan 2026 16:51:46 -0800
Subject: [PATCH 7/7] fixup! be explicit about the conversion error
---
lldb/source/Expression/IRInterpreter.cpp | 12 ++++++++----
.../ir-interpreter/TestIRInterpreter.py | 18 +++++++++++-------
2 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/lldb/source/Expression/IRInterpreter.cpp b/lldb/source/Expression/IRInterpreter.cpp
index 6dd7253eb2f1b..5ca64843d5900 100644
--- a/lldb/source/Expression/IRInterpreter.cpp
+++ b/lldb/source/Expression/IRInterpreter.cpp
@@ -477,8 +477,6 @@ static const char *timeout_error =
"Reached timeout while interpreting expression";
static const char *too_many_functions_error =
"Interpreter doesn't handle modules with multiple function bodies.";
-static const char *bad_conversion_error =
- "Interpreter couldn't convert a value";
static bool CanResolveConstant(llvm::Constant *constant) {
switch (constant->getValueID()) {
@@ -1296,8 +1294,14 @@ bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function,
// Casting floating point values that are out of bounds of the target type
// is undefined behaviour.
if (status & llvm::APFloatBase::opInvalidOp) {
- LLDB_LOG(log, "Couldn't convert {0} to {1}", S, *inst->getType());
- error = lldb_private::Status::FromErrorString(bad_conversion_error);
+ std::string s;
+ raw_string_ostream rso(s);
+ rso << "Conversion error: " << S << " cannot be converted to ";
+ if (inst->getOpcode() == Instruction::FPToUI)
+ rso << "unsigned ";
+ rso << *inst->getType();
+ LLDB_LOGF(log, "%s", s.c_str());
+ error = lldb_private::Status::FromErrorString(s.c_str());
return false;
}
lldb_private::Scalar R(result);
diff --git a/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py b/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
index 206c62cf574c7..88bc29c8f7de8 100644
--- a/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
+++ b/lldb/test/API/commands/expression/ir-interpreter/TestIRInterpreter.py
@@ -250,9 +250,15 @@ def test_fpconv_ub(self):
]
expressions = [
- "(int)$f",
- "(long)$d",
- "(unsigned)$nf",
+ ("(int)$f", "Conversion error: (float) 3.0E+9 cannot be converted to i32"),
+ (
+ "(long)$d",
+ "Conversion error: (float) 1.0E+20 cannot be converted to i64",
+ ),
+ (
+ "(unsigned)$nf",
+ "Conversion error: (float) -1.5 cannot be converted to unsigned i32",
+ ),
]
for expression in set_up_expressions:
@@ -260,10 +266,8 @@ def test_fpconv_ub(self):
# The IR Interpreter returns an error if a value cannot be converted.
for expression in expressions:
- result = target.EvaluateExpression(expression)
- self.assertIn(
- "Interpreter couldn't convert a value", str(result.GetError())
- )
+ result = target.EvaluateExpression(expression[0])
+ self.assertIn(expression[1], str(result.GetError()))
# The conversion should succeed if the destination type can represent the result.
self.expect_expr(
More information about the lldb-commits
mailing list