Skip to content

Commit be4ddf4

Browse files
committed
[lldb] Add some vector operations to the IRInterpreter
This allows the debugger to evaluate expressions without requiring the expression to be CodeGen'd and executed on the target. This should be more efficient for many existing targets but is necessary for targets which are not yet able to evaluate on the target. As far as I know most targets have a vector memory layout that matches the IR element order. Most little endian targets choose to use a little endian element order, and two out of the three big endian targets I know of (MIPS MSA and ARM NEON) choose to use little endian element order even when the elements are big endian which matches LLVM-IR's order. The third is PowerPC Altivec which has the highest indexed element first for big-endian mode. I've attempted to implement the correct element ordering on the relevant operations but I don't really have a means to test the case where the element order doesn't match LLVM-IR's element order so I've chosen to have a guard against element order mismatches to ensure that this change can't break expression evaluation on those targets.
1 parent 4aeb290 commit be4ddf4

File tree

7 files changed

+261
-17
lines changed

7 files changed

+261
-17
lines changed

lldb/include/lldb/Core/Architecture.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,17 @@ class Architecture : public PluginInterface {
129129
RegisterContext &reg_context) const {
130130
return false;
131131
}
132+
133+
// Get the vector element order for this architecture. This determines how
134+
// vector elements are indexed. This matters in a few places such as reading/
135+
// writing LLVM-IR values to/from target memory. Some architectures use
136+
// little-endian element ordering where element 0 is at the lowest address
137+
// even when the architecture is otherwise big-endian (e.g. MIPS MSA, ARM
138+
// NEON), but some architectures like PowerPC may use big-endian element
139+
// ordering where element 0 is at the highest address.
140+
virtual lldb::ByteOrder GetVectorElementOrder() const {
141+
return lldb::eByteOrderLittle;
142+
}
132143
};
133144

134145
} // namespace lldb_private

lldb/include/lldb/Expression/IRInterpreter.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ class IRInterpreter {
3737
public:
3838
static bool CanInterpret(llvm::Module &module, llvm::Function &function,
3939
lldb_private::Status &error,
40-
const bool support_function_calls);
40+
const bool support_function_calls,
41+
lldb_private::ExecutionContext &exe_ctx);
4142

4243
static bool Interpret(llvm::Module &module, llvm::Function &function,
4344
llvm::ArrayRef<lldb::addr_t> args,

lldb/source/Expression/IRInterpreter.cpp

Lines changed: 205 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ static std::string PrintType(const Type *type, bool truncate = false) {
7070
return s;
7171
}
7272

73+
static bool
74+
MemoryMatchesIRElementOrder(lldb_private::ExecutionContext &exe_ctx) {
75+
lldb::TargetSP target_sp = exe_ctx.GetTargetSP();
76+
if (target_sp) {
77+
const auto *arch_plugin = target_sp->GetArchitecturePlugin();
78+
if (arch_plugin) {
79+
return arch_plugin->GetVectorElementOrder() == lldb::eByteOrderLittle;
80+
}
81+
}
82+
return true; // Default to little-endian (matches IR)
83+
}
84+
7385
static bool CanIgnoreCall(const CallInst *call) {
7486
const llvm::Function *called_function = call->getCalledFunction();
7587

@@ -124,6 +136,17 @@ class InterpreterStackFrame {
124136

125137
~InterpreterStackFrame() = default;
126138

139+
bool MemoryMatchesIRElementOrder() {
140+
lldb::TargetSP target_sp = m_execution_unit.GetTarget();
141+
if (target_sp) {
142+
const auto *arch_plugin = target_sp->GetArchitecturePlugin();
143+
if (arch_plugin) {
144+
return arch_plugin->GetVectorElementOrder() == lldb::eByteOrderLittle;
145+
}
146+
}
147+
return true;
148+
}
149+
127150
void Jump(const BasicBlock *bb) {
128151
m_prev_bb = m_bb;
129152
m_bb = bb;
@@ -367,7 +390,65 @@ class InterpreterStackFrame {
367390
return true;
368391
}
369392

393+
bool ResolveVectorConstant(lldb::addr_t process_address,
394+
const Constant *constant) {
395+
auto *vector_type = dyn_cast<FixedVectorType>(constant->getType());
396+
if (!vector_type)
397+
return false;
398+
399+
Type *element_type = vector_type->getElementType();
400+
unsigned num_elements = vector_type->getNumElements();
401+
size_t element_size = m_target_data.getTypeStoreSize(element_type);
402+
size_t total_size = element_size * num_elements;
403+
bool reverse_elements = !MemoryMatchesIRElementOrder();
404+
405+
lldb_private::DataBufferHeap buf(total_size, 0);
406+
uint8_t *data_ptr = buf.GetBytes();
407+
408+
if (isa<ConstantAggregateZero>(constant)) {
409+
// Zero initializer - buffer is already zeroed, just write it
410+
lldb_private::Status write_error;
411+
m_execution_unit.WriteMemory(process_address, buf.GetBytes(),
412+
buf.GetByteSize(), write_error);
413+
return write_error.Success();
414+
}
415+
416+
if (const ConstantDataVector *cdv =
417+
dyn_cast<ConstantDataVector>(constant)) {
418+
for (unsigned i = 0; i < num_elements; ++i) {
419+
const Constant *element = cdv->getElementAsConstant(i);
420+
APInt element_value;
421+
if (!ResolveConstantValue(element_value, element))
422+
return false;
423+
424+
// Calculate target offset based on element ordering
425+
unsigned target_index = !reverse_elements ? i : (num_elements - 1 - i);
426+
size_t offset = target_index * element_size;
427+
428+
lldb_private::Scalar element_scalar(
429+
element_value.zextOrTrunc(element_size * 8));
430+
lldb_private::Status get_data_error;
431+
if (!element_scalar.GetAsMemoryData(data_ptr + offset, element_size,
432+
m_byte_order, get_data_error))
433+
return false;
434+
}
435+
lldb_private::Status write_error;
436+
m_execution_unit.WriteMemory(process_address, buf.GetBytes(),
437+
buf.GetByteSize(), write_error);
438+
439+
return write_error.Success();
440+
}
441+
442+
return false;
443+
}
444+
370445
bool ResolveConstant(lldb::addr_t process_address, const Constant *constant) {
446+
// Handle vector constants specially since they can't be represented as a
447+
// single APInt
448+
if (constant->getType()->isVectorTy()) {
449+
return ResolveVectorConstant(process_address, constant);
450+
}
451+
371452
APInt resolved_value;
372453

373454
if (!ResolveConstantValue(resolved_value, constant))
@@ -484,8 +565,13 @@ static bool CanResolveConstant(llvm::Constant *constant) {
484565
return false;
485566
case Value::ConstantIntVal:
486567
case Value::ConstantFPVal:
568+
return true;
569+
case Value::ConstantDataVectorVal:
487570
case Value::FunctionVal:
488571
return true;
572+
case Value::ConstantAggregateZeroVal:
573+
// Zero initializers can be resolved
574+
return true;
489575
case Value::ConstantExprVal:
490576
if (const ConstantExpr *constant_expr = dyn_cast<ConstantExpr>(constant)) {
491577
switch (constant_expr->getOpcode()) {
@@ -522,7 +608,8 @@ static bool CanResolveConstant(llvm::Constant *constant) {
522608

523609
bool IRInterpreter::CanInterpret(llvm::Module &module, llvm::Function &function,
524610
lldb_private::Status &error,
525-
const bool support_function_calls) {
611+
const bool support_function_calls,
612+
lldb_private::ExecutionContext &exe_ctx) {
526613
lldb_private::Log *log(GetLog(LLDBLog::Expressions));
527614

528615
bool saw_function_with_body = false;
@@ -551,6 +638,7 @@ bool IRInterpreter::CanInterpret(llvm::Module &module, llvm::Function &function,
551638
case Instruction::BitCast:
552639
case Instruction::Br:
553640
case Instruction::PHI:
641+
case Instruction::ExtractElement:
554642
break;
555643
case Instruction::Call: {
556644
CallInst *call_inst = dyn_cast<CallInst>(&ii);
@@ -644,7 +732,25 @@ bool IRInterpreter::CanInterpret(llvm::Module &module, llvm::Function &function,
644732
switch (operand_type->getTypeID()) {
645733
default:
646734
break;
647-
case Type::FixedVectorTyID:
735+
case Type::FixedVectorTyID: {
736+
// If the element order is big-endian (highest index first) then it
737+
// doesn't match LLVM-IR and must be transformed to correctly transfer
738+
// between LLVM-IR and memory. This might not be fully implemented so
739+
// decline to interpret this case.
740+
if (exe_ctx.GetTargetPtr()) {
741+
const auto *arch_plugin =
742+
exe_ctx.GetTargetRef().GetArchitecturePlugin();
743+
if (arch_plugin &&
744+
arch_plugin->GetVectorElementOrder() == lldb::eByteOrderBig) {
745+
LLDB_LOGF(log, "Unsupported big-endian vector element ordering");
746+
error = lldb_private::Status::FromErrorString(
747+
"IR interpreter doesn't support big-endian vector element "
748+
"ordering");
749+
return false;
750+
}
751+
}
752+
break;
753+
}
648754
case Type::ScalableVectorTyID: {
649755
LLDB_LOGF(log, "Unsupported operand type: %s",
650756
PrintType(operand_type).c_str());
@@ -657,8 +763,9 @@ bool IRInterpreter::CanInterpret(llvm::Module &module, llvm::Function &function,
657763
// The IR interpreter currently doesn't know about
658764
// 128-bit integers. As they're not that frequent,
659765
// we can just fall back to the JIT rather than
660-
// choking.
661-
if (operand_type->getPrimitiveSizeInBits() > 64) {
766+
// choking. However, allow vectors since we handle them above.
767+
if (operand_type->getPrimitiveSizeInBits() > 64 &&
768+
!operand_type->isVectorTy()) {
662769
LLDB_LOGF(log, "Unsupported operand type: %s",
663770
PrintType(operand_type).c_str());
664771
error =
@@ -1567,6 +1674,100 @@ bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function,
15671674
frame.AssignValue(inst, returnVal, module);
15681675
}
15691676
} break;
1677+
case Instruction::ExtractElement: {
1678+
const ExtractElementInst *extract_inst = cast<ExtractElementInst>(inst);
1679+
1680+
// Get the vector and index operands
1681+
const Value *vector_operand = extract_inst->getVectorOperand();
1682+
const Value *index_operand = extract_inst->getIndexOperand();
1683+
1684+
// Get the vector address
1685+
lldb::addr_t vector_addr = frame.ResolveValue(vector_operand, module);
1686+
1687+
if (vector_addr == LLDB_INVALID_ADDRESS) {
1688+
LLDB_LOGF(log, "ExtractElement's vector doesn't resolve to anything");
1689+
error = lldb_private::Status::FromErrorString(bad_value_error);
1690+
return false;
1691+
}
1692+
1693+
// Evaluate the index
1694+
lldb_private::Scalar index_scalar;
1695+
if (!frame.EvaluateValue(index_scalar, index_operand, module)) {
1696+
LLDB_LOGF(log, "Couldn't evaluate index %s",
1697+
PrintValue(index_operand).c_str());
1698+
error = lldb_private::Status::FromErrorString(bad_value_error);
1699+
return false;
1700+
}
1701+
1702+
uint64_t index = index_scalar.ULongLong();
1703+
1704+
// Get the vector type information
1705+
auto *vector_type = dyn_cast<FixedVectorType>(vector_operand->getType());
1706+
if (!vector_type) {
1707+
LLDB_LOGF(log, "ExtractElement instruction doesn't have a fixed vector "
1708+
"operand type");
1709+
error =
1710+
lldb_private::Status::FromErrorString(interpreter_internal_error);
1711+
return false;
1712+
}
1713+
1714+
unsigned num_elements = vector_type->getNumElements();
1715+
if (index >= num_elements) {
1716+
LLDB_LOGF(log,
1717+
"ExtractElement index %llu is out of bounds for vector with "
1718+
"%u elements",
1719+
(unsigned long long)index, num_elements);
1720+
error = lldb_private::Status::FromErrorString(bad_value_error);
1721+
return false;
1722+
}
1723+
1724+
Type *element_type = vector_type->getElementType();
1725+
size_t element_size = data_layout.getTypeStoreSize(element_type);
1726+
1727+
// Handle target-specific vector element ordering
1728+
bool reverse_elements = !MemoryMatchesIRElementOrder(exe_ctx);
1729+
uint64_t target_index =
1730+
reverse_elements ? (num_elements - 1 - index) : index;
1731+
size_t element_offset = target_index * element_size;
1732+
1733+
// Allocate space for the result element
1734+
lldb::addr_t result_addr = frame.ResolveValue(extract_inst, module);
1735+
if (result_addr == LLDB_INVALID_ADDRESS) {
1736+
LLDB_LOGF(log, "ExtractElement's result doesn't resolve to anything");
1737+
error = lldb_private::Status::FromErrorString(bad_value_error);
1738+
return false;
1739+
}
1740+
1741+
// Read the element from the vector
1742+
lldb_private::DataBufferHeap element_buffer(element_size, 0);
1743+
lldb_private::Status read_error;
1744+
execution_unit.ReadMemory(element_buffer.GetBytes(),
1745+
vector_addr + element_offset, element_size,
1746+
read_error);
1747+
if (!read_error.Success()) {
1748+
LLDB_LOGF(log, "Couldn't read element data for ExtractElement");
1749+
error = lldb_private::Status::FromErrorString(memory_read_error);
1750+
return false;
1751+
}
1752+
1753+
// Write the element to the result location
1754+
lldb_private::Status write_error;
1755+
execution_unit.WriteMemory(result_addr, element_buffer.GetBytes(),
1756+
element_size, write_error);
1757+
if (!write_error.Success()) {
1758+
LLDB_LOGF(log, "Couldn't write result for ExtractElement");
1759+
error = lldb_private::Status::FromErrorString(memory_write_error);
1760+
return false;
1761+
}
1762+
1763+
if (log) {
1764+
LLDB_LOGF(log, "Interpreted an ExtractElement");
1765+
LLDB_LOGF(log, " Vector: 0x%" PRIx64, vector_addr);
1766+
LLDB_LOGF(log, " Index: %llu", (unsigned long long)index);
1767+
LLDB_LOGF(log, " Element offset: %zu", element_offset);
1768+
LLDB_LOGF(log, " Result: 0x%" PRIx64, result_addr);
1769+
}
1770+
} break;
15701771
}
15711772

15721773
++frame.m_ii;

lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ void ArchitecturePPC64::Terminate() {
3535
std::unique_ptr<Architecture> ArchitecturePPC64::Create(const ArchSpec &arch) {
3636
if (arch.GetTriple().isPPC64() &&
3737
arch.GetTriple().getObjectFormat() == llvm::Triple::ObjectFormatType::ELF)
38-
return std::unique_ptr<Architecture>(new ArchitecturePPC64());
38+
return std::unique_ptr<Architecture>(
39+
new ArchitecturePPC64(arch.GetByteOrder()));
3940
return nullptr;
4041
}
4142

@@ -60,3 +61,7 @@ void ArchitecturePPC64::AdjustBreakpointAddress(const Symbol &func,
6061

6162
addr.SetOffset(addr.GetOffset() + loffs);
6263
}
64+
65+
lldb::ByteOrder ArchitecturePPC64::GetVectorElementOrder() const {
66+
return m_vector_element_order;
67+
}

lldb/source/Plugins/Architecture/PPC64/ArchitecturePPC64.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,14 @@ class ArchitecturePPC64 : public Architecture {
3030
void AdjustBreakpointAddress(const Symbol &func,
3131
Address &addr) const override;
3232

33+
lldb::ByteOrder GetVectorElementOrder() const override;
34+
3335
private:
3436
static std::unique_ptr<Architecture> Create(const ArchSpec &arch);
35-
ArchitecturePPC64() = default;
37+
ArchitecturePPC64(lldb::ByteOrder vector_element_order)
38+
: m_vector_element_order(vector_element_order) {}
39+
40+
lldb::ByteOrder m_vector_element_order;
3641
};
3742

3843
} // namespace lldb_private

lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1523,7 +1523,7 @@ lldb_private::Status ClangExpressionParser::DoPrepareForExecution(
15231523
!process ? false : process->CanInterpretFunctionCalls();
15241524
can_interpret = IRInterpreter::CanInterpret(
15251525
*execution_unit_sp->GetModule(), *execution_unit_sp->GetFunction(),
1526-
interpret_error, interpret_function_calls);
1526+
interpret_error, interpret_function_calls, exe_ctx);
15271527

15281528
if (!can_interpret && execution_policy == eExecutionPolicyNever) {
15291529
err = Status::FromErrorStringWithFormat(

0 commit comments

Comments
 (0)