Skip to content

Commit 9b846c0

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. In terms of memory layout, we have: | Element Values | Element Order | ARM NEON | Endian-dependant | Zero-first | MIPS MSA | Endian-dependant | Zero-first | PowerPC | Endian-dependant | Zero-first | SystemZ | Endian-dependant | Zero-first | Where Zero-first means that element zero of an array/vector is at the lowest memory address. In terms of register layout things, we have: | Element Values | Element Order | Effective Element Order ARM NEON | Endian-dependant | Zero-first | Zero-first MIPS MSA | Endian-dependant | Zero-first | Zero-first PowerPC | Endian-dependant | Endian-dependant* | Zero-first (lane-swaps LE) SystemZ | Endian-dependant | Endian-dependant* | Zero-first (lane-swaps BE) *HW is endian-dependent but CodeGen accounts for it PowerPC is a little more complicated than shown above as it actually supports two modes: True-LE and Big-on-Little and the above table shows True-LE's behaviour. See https://llvm.org/devmtg/2014-10/Slides/Schmidt-SupportingVectorProgramming.pdf I haven't seen evidence that big-on-little is implemented yet so I haven't attempted to account for it. The end result of this is that transferring values between llvm-ir and memory is consistent between the four targets but transferring values between llvm-ir and registers potentially requires transformations. I've therefore: * Redefined GetVectorElementOrder to refer to the register layout not memory * Made Materializer/Dematerializer bail out when reading/writing vector values. * Made bitcast bail out for the cases where a bitcast is a shuffle rather than a nop.
1 parent abbfd8f commit 9b846c0

File tree

8 files changed

+650
-27
lines changed

8 files changed

+650
-27
lines changed

lldb/include/lldb/Core/Architecture.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,11 @@ class Architecture : public PluginInterface {
141141

142142
/// Get the vector element order for this architecture. This determines how
143143
/// vector elements are indexed. This matters in a few places such as reading/
144-
/// writing LLVM-IR values to/from target memory. Some architectures use
145-
/// little-endian element ordering where element 0 is at the lowest address
144+
/// writing LLVM-IR values to/from target registers. Some architectures use
145+
/// little-endian element ordering where element 0 is at the lowest bits
146146
/// even when the architecture is otherwise big-endian (e.g. MIPS MSA, ARM
147147
/// NEON), but some architectures like PowerPC may use big-endian element
148-
/// ordering where element 0 is at the highest address.
148+
/// ordering where element 0 is at the highest bits.
149149
virtual lldb::ByteOrder GetVectorElementOrder() const {
150150
return lldb::eByteOrderLittle;
151151
}

lldb/include/lldb/Expression/IRInterpreter.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
#include "llvm/Pass.h"
1818

1919
namespace llvm {
20+
class DataLayout;
2021
class Function;
2122
class Module;
23+
class Instruction;
2224
}
2325

2426
namespace lldb_private {
@@ -37,7 +39,8 @@ class IRInterpreter {
3739
public:
3840
static bool CanInterpret(llvm::Module &module, llvm::Function &function,
3941
lldb_private::Status &error,
40-
const bool support_function_calls);
42+
const bool support_function_calls,
43+
lldb_private::ExecutionContext &exe_ctx);
4144

4245
static bool Interpret(llvm::Module &module, llvm::Function &function,
4346
llvm::ArrayRef<lldb::addr_t> args,
@@ -51,6 +54,12 @@ class IRInterpreter {
5154
private:
5255
static bool supportsFunction(llvm::Function &llvm_function,
5356
lldb_private::Status &err);
57+
58+
static bool InterpretExtractElement(
59+
const llvm::Instruction *inst, class InterpreterStackFrame &frame,
60+
const llvm::DataLayout &data_layout, llvm::Module &module,
61+
lldb_private::IRExecutionUnit &execution_unit,
62+
lldb_private::Status &error, lldb_private::Log *log);
5463
};
5564

5665
#endif

lldb/source/Expression/IRInterpreter.cpp

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

73+
static bool
74+
isNonTrivialBitcast(const Instruction &inst,
75+
lldb_private::ExecutionContext &exe_ctx) {
76+
auto *result_type = dyn_cast<VectorType>(inst.getType());
77+
auto *operand = inst.getOperand(0);
78+
auto *operand_type = dyn_cast<VectorType>(operand->getType());
79+
80+
// If neither type is a vector then the bitcast is trivial
81+
if (!result_type && !operand_type)
82+
return false;
83+
84+
// Get endianness and element order from the architecture
85+
lldb::ByteOrder byte_order = lldb::eByteOrderLittle;
86+
lldb::ByteOrder element_order = lldb::eByteOrderLittle;
87+
88+
lldb::TargetSP target_sp = exe_ctx.GetTargetSP();
89+
if (target_sp) {
90+
const auto *arch_plugin = target_sp->GetArchitecturePlugin();
91+
if (arch_plugin) {
92+
byte_order = target_sp->GetArchitecture().GetByteOrder();
93+
element_order = arch_plugin->GetVectorElementOrder();
94+
}
95+
}
96+
97+
// Bitcast is trivial if endianness matches element order
98+
if (byte_order == element_order)
99+
return false;
100+
101+
// If the element order and value byte order disagree then vector bitcasts
102+
// aren't no-ops when the element sizes change. For example given:
103+
// <2 x i32> <i32 0x00112233, i32 0x44556677>
104+
// then bitcast's LangRef definition of
105+
// store <2 x i32>, ptr @loc
106+
// load <4 x i16>, ptr @loc
107+
// gives:
108+
// Order | Mem after Store | <4 x i16> after Load | Bitcast
109+
// LF | 3322110077665544 | 0x3322 0x0011 0x6677 0x4455 | no-op
110+
// BN | 0011223344556677 | 0x0011 0x2233 0x4455 0x6677 | shuffle
111+
// LF | 7766554433221100 | 0x6677 0x4455 0x3322 0x1100 | shuffle
112+
// BN | 4455667700112233 | 0x4455 0x6677 0x0011 0x2233 | no-op
113+
// Order abbreviations:
114+
// L = Little Endian
115+
// B = Big Endian
116+
// F = Lane 0 is first
117+
// N = Lane N-1 is first
118+
119+
// If only one type is a vector, then we'll assume it's non-trivial on the
120+
// basis that this changes the number of elements from N to 1 or the other
121+
// way around.
122+
if (!result_type || !operand_type)
123+
return false;
124+
125+
// I'm not sure how scalable vectors behave in this situation.
126+
// Reject them to be safe.
127+
if (!result_type->getElementCount().isFixed() ||
128+
!operand_type->getElementCount().isFixed())
129+
return true;
130+
131+
// We can handle the cases that are no-op by virtue of the element
132+
// sizes/counts not changing but the shuffle cases aren't
133+
// implemented in IRInterpreter::Interpret so decline to interpret
134+
// them.
135+
if (result_type->getElementCount() != operand_type->getElementCount() ||
136+
result_type->getScalarSizeInBits() != operand_type->getScalarSizeInBits())
137+
return true;
138+
139+
return false;
140+
}
141+
73142
static bool CanIgnoreCall(const CallInst *call) {
74143
const llvm::Function *called_function = call->getCalledFunction();
75144

@@ -367,7 +436,62 @@ class InterpreterStackFrame {
367436
return true;
368437
}
369438

439+
bool ResolveVectorConstant(lldb::addr_t process_address,
440+
const Constant *constant) {
441+
auto *vector_type = dyn_cast<FixedVectorType>(constant->getType());
442+
if (!vector_type)
443+
return false;
444+
445+
Type *element_type = vector_type->getElementType();
446+
unsigned num_elements = vector_type->getNumElements();
447+
size_t element_size = m_target_data.getTypeStoreSize(element_type);
448+
size_t total_size = element_size * num_elements;
449+
450+
lldb_private::DataBufferHeap buf(total_size, 0);
451+
uint8_t *data_ptr = buf.GetBytes();
452+
453+
if (isa<ConstantAggregateZero>(constant)) {
454+
// Zero initializer - buffer is already zeroed, just write it
455+
lldb_private::Status write_error;
456+
m_execution_unit.WriteMemory(process_address, buf.GetBytes(),
457+
buf.GetByteSize(), write_error);
458+
return write_error.Success();
459+
}
460+
461+
if (const ConstantDataVector *cdv =
462+
dyn_cast<ConstantDataVector>(constant)) {
463+
for (unsigned i = 0; i < num_elements; ++i) {
464+
const Constant *element = cdv->getElementAsConstant(i);
465+
APInt element_value;
466+
if (!ResolveConstantValue(element_value, element))
467+
return false;
468+
469+
size_t offset = i * element_size;
470+
471+
lldb_private::Scalar element_scalar(
472+
element_value.zextOrTrunc(element_size * 8));
473+
lldb_private::Status get_data_error;
474+
if (!element_scalar.GetAsMemoryData(data_ptr + offset, element_size,
475+
m_byte_order, get_data_error))
476+
return false;
477+
}
478+
lldb_private::Status write_error;
479+
m_execution_unit.WriteMemory(process_address, buf.GetBytes(),
480+
buf.GetByteSize(), write_error);
481+
482+
return write_error.Success();
483+
}
484+
485+
return false;
486+
}
487+
370488
bool ResolveConstant(lldb::addr_t process_address, const Constant *constant) {
489+
// Handle vector constants specially since they can't be represented as a
490+
// single APInt
491+
if (constant->getType()->isVectorTy()) {
492+
return ResolveVectorConstant(process_address, constant);
493+
}
494+
371495
APInt resolved_value;
372496

373497
if (!ResolveConstantValue(resolved_value, constant))
@@ -484,8 +608,12 @@ static bool CanResolveConstant(llvm::Constant *constant) {
484608
return false;
485609
case Value::ConstantIntVal:
486610
case Value::ConstantFPVal:
611+
return true;
487612
case Value::FunctionVal:
488613
return true;
614+
case Value::ConstantDataVectorVal:
615+
case Value::ConstantAggregateZeroVal:
616+
return constant->getType()->getTypeID() == Type::FixedVectorTyID;
489617
case Value::ConstantExprVal:
490618
if (const ConstantExpr *constant_expr = dyn_cast<ConstantExpr>(constant)) {
491619
switch (constant_expr->getOpcode()) {
@@ -522,7 +650,8 @@ static bool CanResolveConstant(llvm::Constant *constant) {
522650

523651
bool IRInterpreter::CanInterpret(llvm::Module &module, llvm::Function &function,
524652
lldb_private::Status &error,
525-
const bool support_function_calls) {
653+
const bool support_function_calls,
654+
lldb_private::ExecutionContext &exe_ctx) {
526655
lldb_private::Log *log(GetLog(LLDBLog::Expressions));
527656

528657
bool saw_function_with_body = false;
@@ -548,9 +677,13 @@ bool IRInterpreter::CanInterpret(llvm::Module &module, llvm::Function &function,
548677
}
549678
case Instruction::Add:
550679
case Instruction::Alloca:
551-
case Instruction::BitCast:
552680
case Instruction::Br:
553681
case Instruction::PHI:
682+
case Instruction::ExtractElement:
683+
break;
684+
case Instruction::BitCast:
685+
if (isNonTrivialBitcast(ii, exe_ctx))
686+
return false;
554687
break;
555688
case Instruction::Call: {
556689
CallInst *call_inst = dyn_cast<CallInst>(&ii);
@@ -645,6 +778,7 @@ bool IRInterpreter::CanInterpret(llvm::Module &module, llvm::Function &function,
645778
default:
646779
break;
647780
case Type::FixedVectorTyID:
781+
break;
648782
case Type::ScalableVectorTyID: {
649783
LLDB_LOGF(log, "Unsupported operand type: %s",
650784
PrintType(operand_type).c_str());
@@ -657,8 +791,9 @@ bool IRInterpreter::CanInterpret(llvm::Module &module, llvm::Function &function,
657791
// The IR interpreter currently doesn't know about
658792
// 128-bit integers. As they're not that frequent,
659793
// we can just fall back to the JIT rather than
660-
// choking.
661-
if (operand_type->getPrimitiveSizeInBits() > 64) {
794+
// choking. However, allow vectors since we handle them above.
795+
if (operand_type->getPrimitiveSizeInBits() > 64 &&
796+
!operand_type->isVectorTy()) {
662797
LLDB_LOGF(log, "Unsupported operand type: %s",
663798
PrintType(operand_type).c_str());
664799
error =
@@ -1543,9 +1678,7 @@ bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function,
15431678
// Void return type
15441679
if (returnType->isVoidTy()) {
15451680
// Cant assign to void types, so we leave the frame untouched
1546-
} else
1547-
// Integer or pointer return type
1548-
if (returnType->isIntegerTy() || returnType->isPointerTy()) {
1681+
} else if (returnType->isIntegerTy() || returnType->isPointerTy()) {
15491682
// Get the encapsulated return value
15501683
lldb::ValueObjectSP retVal = call_plan_sp.get()->GetReturnValueObject();
15511684

@@ -1567,10 +1700,113 @@ bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function,
15671700
frame.AssignValue(inst, returnVal, module);
15681701
}
15691702
} break;
1703+
case Instruction::ExtractElement: {
1704+
if (!InterpretExtractElement(inst, frame, data_layout, module,
1705+
execution_unit, error, log))
1706+
return false;
1707+
} break;
15701708
}
15711709

15721710
++frame.m_ii;
15731711
}
15741712

15751713
return false;
15761714
}
1715+
1716+
bool IRInterpreter::InterpretExtractElement(
1717+
const llvm::Instruction *inst, InterpreterStackFrame &frame,
1718+
const llvm::DataLayout &data_layout, llvm::Module &module,
1719+
lldb_private::IRExecutionUnit &execution_unit, lldb_private::Status &error,
1720+
lldb_private::Log *log) {
1721+
const ExtractElementInst *extract_inst = cast<ExtractElementInst>(inst);
1722+
1723+
// Get the vector and index operands
1724+
const Value *vector_operand = extract_inst->getVectorOperand();
1725+
const Value *index_operand = extract_inst->getIndexOperand();
1726+
1727+
// Get the vector address
1728+
lldb::addr_t vector_addr =
1729+
frame.ResolveValue(vector_operand, module);
1730+
1731+
if (vector_addr == LLDB_INVALID_ADDRESS) {
1732+
LLDB_LOGF(log, "ExtractElement's vector doesn't resolve to anything");
1733+
error = lldb_private::Status::FromErrorString(bad_value_error);
1734+
return false;
1735+
}
1736+
1737+
// Evaluate the index
1738+
lldb_private::Scalar index_scalar;
1739+
if (!frame.EvaluateValue(index_scalar, index_operand, module)) {
1740+
LLDB_LOGF(log, "Couldn't evaluate index %s",
1741+
PrintValue(index_operand).c_str());
1742+
error = lldb_private::Status::FromErrorString(bad_value_error);
1743+
return false;
1744+
}
1745+
1746+
uint64_t index = index_scalar.ULongLong();
1747+
1748+
// Get the vector type information
1749+
auto *vector_type = dyn_cast<FixedVectorType>(vector_operand->getType());
1750+
if (!vector_type) {
1751+
LLDB_LOGF(log, "ExtractElement instruction doesn't have a fixed vector "
1752+
"operand type");
1753+
error = lldb_private::Status::FromErrorString(interpreter_internal_error);
1754+
return false;
1755+
}
1756+
1757+
unsigned num_elements = vector_type->getNumElements();
1758+
if (index >= num_elements) {
1759+
LLDB_LOG(log,
1760+
"ExtractElement index {0} is out of bounds for vector with "
1761+
"{1} elements",
1762+
index, num_elements);
1763+
error = lldb_private::Status::FromErrorString(bad_value_error);
1764+
return false;
1765+
}
1766+
1767+
Type *element_type = vector_type->getElementType();
1768+
size_t element_size = data_layout.getTypeStoreSize(element_type);
1769+
1770+
size_t element_offset = index * element_size;
1771+
1772+
// Allocate space for the result element
1773+
lldb::addr_t result_addr = frame.ResolveValue(extract_inst, module);
1774+
if (result_addr == LLDB_INVALID_ADDRESS) {
1775+
LLDB_LOG(log, "ExtractElement's result doesn't resolve to anything");
1776+
error = lldb_private::Status::FromErrorString(bad_value_error);
1777+
return false;
1778+
}
1779+
1780+
// Read the element from the vector
1781+
lldb_private::DataBufferHeap element_buffer(element_size, 0);
1782+
lldb_private::Status read_error;
1783+
execution_unit.ReadMemory(element_buffer.GetBytes(),
1784+
vector_addr + element_offset, element_size,
1785+
read_error);
1786+
if (!read_error.Success()) {
1787+
LLDB_LOG(log, "Couldn't read element data for ExtractElement");
1788+
error = lldb_private::Status::FromErrorString(memory_read_error);
1789+
return false;
1790+
}
1791+
1792+
// Write the element to the result location
1793+
lldb_private::Status write_error;
1794+
execution_unit.WriteMemory(result_addr, element_buffer.GetBytes(),
1795+
element_size, write_error);
1796+
if (!write_error.Success()) {
1797+
LLDB_LOG(log, "Couldn't write result for ExtractElement");
1798+
error = lldb_private::Status::FromErrorString(memory_write_error);
1799+
return false;
1800+
}
1801+
1802+
if (log) {
1803+
LLDB_LOG(log,
1804+
"Interpreted an ExtractElement\n"
1805+
" Vector: {0}\n"
1806+
" Index: {1}\n"
1807+
" Element offset: {2}\n"
1808+
" Result: {3}\n",
1809+
vector_addr, index, element_offset, result_addr);
1810+
}
1811+
return true;
1812+
}

0 commit comments

Comments
 (0)