[Lldb-commits] [lldb] [lldb] Speed up `SBValue::CreateValueFromExpression` by using DIL (PR #222192)
Ilia Kuklin via lldb-commits
lldb-commits at lists.llvm.org
Sat Sep 12 06:10:22 PDT 2026
https://github.com/kuilpd updated https://github.com/llvm/llvm-project/pull/222192
>From 858d4947fa11931f15d1fe57686065c4d812da66 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Mon, 7 Sep 2026 21:10:14 +0500
Subject: [PATCH 1/5] [lldb] Use DIL in SBValue::CreateValueFromExpression
---
lldb/include/lldb/Target/Target.h | 4 ++
lldb/source/API/SBValue.cpp | 49 ++++++++++++++++--
lldb/source/Target/Target.cpp | 22 ++++++++
lldb/source/Target/TargetProperties.td | 3 ++
.../expr/CreateValueFromExpression/Makefile | 3 ++
.../TestCreateValueFromExpression.py | 51 +++++++++++++++++++
.../expr/CreateValueFromExpression/main.cpp | 7 +++
7 files changed, 134 insertions(+), 5 deletions(-)
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/CreateValueFromExpression/Makefile
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/CreateValueFromExpression/TestCreateValueFromExpression.py
create mode 100644 lldb/test/API/commands/frame/var-dil/expr/CreateValueFromExpression/main.cpp
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 31a59a8501338..6ee72bf0d288b 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -281,6 +281,10 @@ class TargetProperties : public Properties {
void SetUseDIL(ExecutionContext *exe_ctx, bool b);
+ bool GetUseDILForCreatingValues() const;
+
+ void SetUseDILForCreatingValues(bool b);
+
void SetRequireHardwareBreakpoints(bool b);
bool GetRequireHardwareBreakpoints() const;
diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp
index 32fbe1ecf15b0..822e18df7d199 100644
--- a/lldb/source/API/SBValue.cpp
+++ b/lldb/source/API/SBValue.cpp
@@ -473,18 +473,57 @@ lldb::SBValue SBValue::CreateValueFromExpression(const char *name,
SBExpressionOptions &options) {
LLDB_INSTRUMENT_VA(this, name, expression, options);
- lldb::SBValue sb_value;
+ lldb::ValueObjectSP new_value_sp;
+ StackFrameSP frame_sp(GetFrame().GetFrameSP());
+ TargetSP target_sp(GetTarget().GetSP());
+ // If enabled, attempt to use DIL to evaluate the expression.
+ bool DIL_success = false;
+ if (frame_sp && target_sp) {
+ bool use_DIL = target_sp->GetUseDILForCreatingValues();
+ if (use_DIL) {
+ Status error;
+ if (frame_sp) {
+ uint32_t expr_path_options =
+ StackFrame::eExpressionPathOptionCheckPtrVsMember |
+ StackFrame::eExpressionPathOptionsAllowDirectIVarAccess;
+ lldb::VariableSP var_sp;
+ new_value_sp = frame_sp->GetValueForVariableExpressionPath(
+ expression, eNoDynamicValues, expr_path_options, var_sp, error);
+ }
+ DIL_success = new_value_sp && new_value_sp->GetError().Success();
+ }
+ }
+
ValueLocker locker;
lldb::ValueObjectSP value_sp(GetSP(locker));
- lldb::ValueObjectSP new_value_sp;
- if (value_sp) {
+ // Fall back to full expression evaluation if DIL did not succeed.
+ if (!DIL_success && value_sp) {
ExecutionContext exe_ctx(value_sp->GetExecutionContextRef());
new_value_sp = value_sp->CreateChildValueObjectFromExpression(
name, expression, exe_ctx, options.ref());
- if (new_value_sp)
- new_value_sp->SetName(name);
}
+
+ if (new_value_sp)
+ new_value_sp->SetName(name);
+ lldb::SBValue sb_value;
sb_value.SetSP(new_value_sp);
+
+ Log *log = GetLog(LLDBLog::Expressions);
+ if (new_value_sp && new_value_sp->GetError().Success())
+ LLDB_LOGF(log,
+ "** [SBValue::CreateValueFromExpression] Expression result: "
+ "(%s) %s = %s (evaluated by: %s) **",
+ new_value_sp->GetTypeName().GetCString(),
+ new_value_sp->GetName().GetCString(),
+ new_value_sp->GetValueAsCString(),
+ DIL_success ? "DIL" : "UserExpression");
+ else
+ LLDB_LOGF(log,
+ "** [SBValue::CreateValueFromExpression] Expression evaluation "
+ "failed: %s **",
+ new_value_sp ? new_value_sp->GetError().AsCString()
+ : "unknown error");
+
return sb_value;
}
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 928292799be21..20c94d98eca56 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -5223,6 +5223,28 @@ void TargetProperties::SetUseDIL(ExecutionContext *exe_ctx, bool b) {
exp_values->SetPropertyAtIndex(ePropertyUseDIL, true, exe_ctx);
}
+bool TargetProperties::GetUseDILForCreatingValues() const {
+ const Property *exp_property =
+ m_collection_sp->GetPropertyAtIndex(ePropertyExperimental);
+ OptionValueProperties *exp_values =
+ exp_property->GetValue()->GetAsProperties();
+ if (exp_values)
+ return exp_values
+ ->GetPropertyAtIndexAs<bool>(ePropertyUseDILForCreatingValues)
+ .value_or(false);
+ else
+ return true;
+}
+
+void TargetProperties::SetUseDILForCreatingValues(bool b) {
+ const Property *exp_property =
+ m_collection_sp->GetPropertyAtIndex(ePropertyExperimental);
+ OptionValueProperties *exp_values =
+ exp_property->GetValue()->GetAsProperties();
+ if (exp_values)
+ exp_values->SetPropertyAtIndex(ePropertyUseDILForCreatingValues, b);
+}
+
ArchSpec TargetProperties::GetDefaultArchitecture() const {
const uint32_t idx = ePropertyDefaultArch;
return GetPropertyAtIndexAs<ArchSpec>(idx, {});
diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td
index 7b296da45b26e..3d717fe334b44 100644
--- a/lldb/source/Target/TargetProperties.td
+++ b/lldb/source/Target/TargetProperties.td
@@ -7,6 +7,9 @@ let Definition = "target_experimental", Path = "target.experimental" in {
def UseDIL : Property<"use-DIL", "Boolean">,
Global, DefaultTrue,
Desc<"If true, use the DIL implementation for frame variable evaluation.">;
+ def UseDILForCreatingValues: Property<"use-DIL-for-creating-values", "Boolean">,
+ Global, DefaultTrue,
+ Desc<"If true, use the DIL implementation to create variables from expressions.">;
}
let Definition = "target", Path = "target" in {
diff --git a/lldb/test/API/commands/frame/var-dil/expr/CreateValueFromExpression/Makefile b/lldb/test/API/commands/frame/var-dil/expr/CreateValueFromExpression/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/CreateValueFromExpression/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var-dil/expr/CreateValueFromExpression/TestCreateValueFromExpression.py b/lldb/test/API/commands/frame/var-dil/expr/CreateValueFromExpression/TestCreateValueFromExpression.py
new file mode 100644
index 0000000000000..bfc202c96772f
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/CreateValueFromExpression/TestCreateValueFromExpression.py
@@ -0,0 +1,51 @@
+"""
+Test how DIL is used in SBValue::CreateValueFromExpression.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+
+class TestCreateValueFromExpression(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_formatter(self):
+ self.build()
+ (_, _, thread, _) = lldbutil.run_to_source_breakpoint(
+ self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+ )
+
+ # Enable logging
+ log_file = self.getBuildArtifact("log-file.txt")
+ if os.path.exists(log_file):
+ os.remove(log_file)
+ self.runCmd("log enable -f '%s' lldb expr" % (log_file))
+
+ self.runCmd("settings set target.experimental.use-DIL true")
+ self.runCmd("settings set target.experimental.use-DIL-for-creating-values true")
+
+ # Check expression results
+ frame = thread.GetFrameAtIndex(0)
+ i = frame.FindVariable("i")
+ v1 = i.CreateValueFromExpression("v1", "i + 1")
+ self.assertEqual(v1.GetValue(), "1")
+ v2 = i.CreateValueFromExpression("v2", "static_cast<double>(i) + 2.5")
+ self.assertEqual(v2.GetValue(), "2.5")
+ self.runCmd(
+ "settings set target.experimental.use-DIL-for-creating-values false"
+ )
+ v3 = i.CreateValueFromExpression("v3", "i + 3")
+ self.assertEqual(v3.GetValue(), "3")
+
+ with open(log_file, "r") as f:
+ log = f.read()
+
+ # Check that supported expression is evaluated by DIL
+ self.assertGreater(log.find("v1 = 1 (evaluated by: DIL)"), 0)
+ # Check that if DIL cannot evaluate the expression, it falls back to
+ # full expression evaluation
+ self.assertGreater(log.find("v2 = 2.5 (evaluated by: UserExpression)"), 0)
+ # Check that creating values using DIL was disabled for the 3rd expression
+ self.assertGreater(log.find("v3 = 3 (evaluated by: UserExpression)"), 0)
diff --git a/lldb/test/API/commands/frame/var-dil/expr/CreateValueFromExpression/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/CreateValueFromExpression/main.cpp
new file mode 100644
index 0000000000000..0435ca02d5605
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/CreateValueFromExpression/main.cpp
@@ -0,0 +1,7 @@
+void stop() {}
+
+int main() {
+ int i = 0;
+
+ stop(); // Set a breakpoint here
+}
>From a84f9142a88933bc8a12b62106f4254cd665b98e Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Wed, 9 Sep 2026 21:00:02 +0500
Subject: [PATCH 2/5] Remove a check, pass the dynamic value option
---
lldb/source/API/SBValue.cpp | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp
index 822e18df7d199..1d1263060ff69 100644
--- a/lldb/source/API/SBValue.cpp
+++ b/lldb/source/API/SBValue.cpp
@@ -482,14 +482,13 @@ lldb::SBValue SBValue::CreateValueFromExpression(const char *name,
bool use_DIL = target_sp->GetUseDILForCreatingValues();
if (use_DIL) {
Status error;
- if (frame_sp) {
- uint32_t expr_path_options =
- StackFrame::eExpressionPathOptionCheckPtrVsMember |
- StackFrame::eExpressionPathOptionsAllowDirectIVarAccess;
- lldb::VariableSP var_sp;
- new_value_sp = frame_sp->GetValueForVariableExpressionPath(
- expression, eNoDynamicValues, expr_path_options, var_sp, error);
- }
+ uint32_t expr_path_options =
+ StackFrame::eExpressionPathOptionCheckPtrVsMember |
+ StackFrame::eExpressionPathOptionsAllowDirectIVarAccess;
+ lldb::VariableSP var_sp;
+ new_value_sp = frame_sp->GetValueForVariableExpressionPath(
+ expression, options.GetFetchDynamicValue(), expr_path_options, var_sp,
+ error);
DIL_success = new_value_sp && new_value_sp->GetError().Success();
}
}
>From 6b6088bcae4524a0c26a81a418048bbaf6c6a435 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Wed, 9 Sep 2026 21:51:07 +0500
Subject: [PATCH 3/5] Add comments and documentation
---
lldb/bindings/interface/SBValueDocstrings.i | 9 +++++++++
lldb/include/lldb/API/SBValue.h | 6 ++++++
lldb/source/Target/TargetProperties.td | 2 +-
3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/lldb/bindings/interface/SBValueDocstrings.i b/lldb/bindings/interface/SBValueDocstrings.i
index 627036b6c0c66..08e697e2bf7b2 100644
--- a/lldb/bindings/interface/SBValueDocstrings.i
+++ b/lldb/bindings/interface/SBValueDocstrings.i
@@ -230,3 +230,12 @@ linked list."
or a constant. A success result does not guarantee a write will succeed;
other runtime conditions may still prevent a successful write."
) lldb::SBValue::CanSet;
+
+%feature("docstring", "
+ Create an SBValue with the given name by evaluating the expression, with
+ the execution context inherited from the current SBValue.
+ Data Inspection Language (DIL) attempts to evaluate the expression first
+ (can be disabled by setting target.experimental.use-DIL-for-creating-values
+ to false). If DIL is not called or fails, the evaluation falls back to
+ UserExpression."
+) lldb::SBValue::CreateValueFromExpression;
diff --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h
index bbaa28762328e..fe2b90e305196 100644
--- a/lldb/include/lldb/API/SBValue.h
+++ b/lldb/include/lldb/API/SBValue.h
@@ -165,6 +165,12 @@ class LLDB_API SBValue {
LLDB_DEPRECATED("Use the expression evaluator to perform type casting")
lldb::SBValue Cast(lldb::SBType type);
+ /// Create an SBValue with the given name by evaluating the expression, with
+ /// the execution context inherited from the current SBValue.
+ /// Data Inspection Language (DIL) attempts to evaluate the expression first
+ /// (can be disabled by target.experimental.use-DIL-for-creating-values)
+ /// If DIL is not called or fails, the evaluation falls back to
+ /// UserExpression.
lldb::SBValue CreateValueFromExpression(const char *name,
const char *expression);
diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td
index 3d717fe334b44..4ba6791e3183d 100644
--- a/lldb/source/Target/TargetProperties.td
+++ b/lldb/source/Target/TargetProperties.td
@@ -9,7 +9,7 @@ let Definition = "target_experimental", Path = "target.experimental" in {
Desc<"If true, use the DIL implementation for frame variable evaluation.">;
def UseDILForCreatingValues: Property<"use-DIL-for-creating-values", "Boolean">,
Global, DefaultTrue,
- Desc<"If true, use the DIL implementation to create variables from expressions.">;
+ Desc<"If true, use the DIL implementation in API SBValue::CreateValueFromExpression.">;
}
let Definition = "target", Path = "target" in {
>From 1bf67a332449dba260d9fd6b9f7a124722c3e8f3 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Fri, 11 Sep 2026 21:01:05 +0500
Subject: [PATCH 4/5] Add a DIL option to SBExpressionOptions and use it
---
lldb/include/lldb/API/SBExpressionOptions.h | 4 ++++
lldb/include/lldb/Target/Target.h | 8 ++++++++
lldb/source/API/SBExpressionOptions.cpp | 12 ++++++++++++
lldb/source/API/SBValue.cpp | 6 ++++--
.../TestCreateValueFromExpression.py | 16 +++++++++++++---
5 files changed, 41 insertions(+), 5 deletions(-)
diff --git a/lldb/include/lldb/API/SBExpressionOptions.h b/lldb/include/lldb/API/SBExpressionOptions.h
index 8dcd6ea8e511a..96adc084d86f1 100644
--- a/lldb/include/lldb/API/SBExpressionOptions.h
+++ b/lldb/include/lldb/API/SBExpressionOptions.h
@@ -115,6 +115,10 @@ class LLDB_API SBExpressionOptions {
SBError SetBooleanLanguageOption(const char *option_name, bool value);
+ bool GetTryDILFirst();
+
+ void SetTryDILFirst(bool b = true);
+
protected:
lldb_private::EvaluateExpressionOptions *get() const;
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 6ee72bf0d288b..0b4290ad4a33d 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -534,6 +534,10 @@ class EvaluateExpressionOptions {
bool GetCppIgnoreContextQualifiers() const;
+ void SetTryDILFirst(bool b) { m_try_DIL_first = b; }
+
+ bool GetTryDILFirst() const { return m_try_DIL_first; }
+
private:
const StructuredData::Dictionary &GetLanguageOptions() const;
@@ -560,6 +564,10 @@ class EvaluateExpressionOptions {
/// True if the executed code should be treated as utility code that is only
/// used by LLDB internally.
bool m_running_utility_expression = false;
+ /// If enabled, Data Inspection Language (DIL) should attempt to evaluate the
+ /// expression first. If DIL is not called or fails, the evaluation falls
+ /// back to UserExpression.
+ bool m_try_DIL_first = false;
lldb::DynamicValueType m_use_dynamic = lldb::eNoDynamicValues;
Timeout<std::micro> m_timeout = default_timeout;
diff --git a/lldb/source/API/SBExpressionOptions.cpp b/lldb/source/API/SBExpressionOptions.cpp
index 7786daa98d1cb..36afca66debfe 100644
--- a/lldb/source/API/SBExpressionOptions.cpp
+++ b/lldb/source/API/SBExpressionOptions.cpp
@@ -301,6 +301,18 @@ SBError SBExpressionOptions::SetBooleanLanguageOption(const char *option_name,
return error;
}
+bool SBExpressionOptions::GetTryDILFirst() {
+ LLDB_INSTRUMENT_VA(this);
+
+ return m_opaque_up->GetTryDILFirst();
+}
+
+void SBExpressionOptions::SetTryDILFirst(bool b) {
+ LLDB_INSTRUMENT_VA(this, b);
+
+ return m_opaque_up->SetTryDILFirst(b);
+}
+
EvaluateExpressionOptions *SBExpressionOptions::get() const {
return m_opaque_up.get();
}
diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp
index 1d1263060ff69..449cafb85ef64 100644
--- a/lldb/source/API/SBValue.cpp
+++ b/lldb/source/API/SBValue.cpp
@@ -465,6 +465,9 @@ lldb::SBValue SBValue::CreateValueFromExpression(const char *name,
SBExpressionOptions options;
options.ref().SetKeepInMemory(true);
+ TargetSP target_sp(GetTarget().GetSP());
+ if (target_sp)
+ options.SetTryDILFirst(target_sp->GetUseDILForCreatingValues());
return CreateValueFromExpression(name, expression, options);
}
@@ -479,8 +482,7 @@ lldb::SBValue SBValue::CreateValueFromExpression(const char *name,
// If enabled, attempt to use DIL to evaluate the expression.
bool DIL_success = false;
if (frame_sp && target_sp) {
- bool use_DIL = target_sp->GetUseDILForCreatingValues();
- if (use_DIL) {
+ if (options.GetTryDILFirst()) {
Status error;
uint32_t expr_path_options =
StackFrame::eExpressionPathOptionCheckPtrVsMember |
diff --git a/lldb/test/API/commands/frame/var-dil/expr/CreateValueFromExpression/TestCreateValueFromExpression.py b/lldb/test/API/commands/frame/var-dil/expr/CreateValueFromExpression/TestCreateValueFromExpression.py
index bfc202c96772f..f105adc87ac14 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/CreateValueFromExpression/TestCreateValueFromExpression.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/CreateValueFromExpression/TestCreateValueFromExpression.py
@@ -33,11 +33,17 @@ def test_formatter(self):
self.assertEqual(v1.GetValue(), "1")
v2 = i.CreateValueFromExpression("v2", "static_cast<double>(i) + 2.5")
self.assertEqual(v2.GetValue(), "2.5")
+ expr_options = lldb.SBExpressionOptions()
+ v3 = i.CreateValueFromExpression("v3", "i + 3", expr_options)
+ self.assertEqual(v3.GetValue(), "3")
self.runCmd(
"settings set target.experimental.use-DIL-for-creating-values false"
)
- v3 = i.CreateValueFromExpression("v3", "i + 3")
- self.assertEqual(v3.GetValue(), "3")
+ v4 = i.CreateValueFromExpression("v4", "i + 4")
+ self.assertEqual(v4.GetValue(), "4")
+ expr_options.SetTryDILFirst(True)
+ v5 = i.CreateValueFromExpression("v5", "i + 5", expr_options)
+ self.assertEqual(v5.GetValue(), "5")
with open(log_file, "r") as f:
log = f.read()
@@ -47,5 +53,9 @@ def test_formatter(self):
# Check that if DIL cannot evaluate the expression, it falls back to
# full expression evaluation
self.assertGreater(log.find("v2 = 2.5 (evaluated by: UserExpression)"), 0)
- # Check that creating values using DIL was disabled for the 3rd expression
+ # Check that trying DIL can be disabled through expression options parameter
self.assertGreater(log.find("v3 = 3 (evaluated by: UserExpression)"), 0)
+ # Check that using DIL was disabled after disabling the lldb setting
+ self.assertGreater(log.find("v4 = 4 (evaluated by: UserExpression)"), 0)
+ # Check that trying DIL can be enabled through expression options parameter
+ self.assertGreater(log.find("v5 = 5 (evaluated by: DIL)"), 0)
>From 641cbed0644f828ab64a8a05e50b3b51228887ec Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Sat, 12 Sep 2026 18:07:59 +0500
Subject: [PATCH 5/5] Remove unused return
---
lldb/source/API/SBExpressionOptions.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/source/API/SBExpressionOptions.cpp b/lldb/source/API/SBExpressionOptions.cpp
index 36afca66debfe..9fbd1c70f9d57 100644
--- a/lldb/source/API/SBExpressionOptions.cpp
+++ b/lldb/source/API/SBExpressionOptions.cpp
@@ -310,7 +310,7 @@ bool SBExpressionOptions::GetTryDILFirst() {
void SBExpressionOptions::SetTryDILFirst(bool b) {
LLDB_INSTRUMENT_VA(this, b);
- return m_opaque_up->SetTryDILFirst(b);
+ m_opaque_up->SetTryDILFirst(b);
}
EvaluateExpressionOptions *SBExpressionOptions::get() const {
More information about the lldb-commits
mailing list