aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp')
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp1771
1 files changed, 1771 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp
new file mode 100644
index 000000000000..cc9bd14c6194
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp
@@ -0,0 +1,1771 @@
+//===-- IRForTarget.cpp ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "IRForTarget.h"
+
+#include "ClangExpressionDeclMap.h"
+#include "ClangUtil.h"
+
+#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/Operator.h"
+#include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/ValueSymbolTable.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/IPO.h"
+
+#include "clang/AST/ASTContext.h"
+
+#include "lldb/Core/dwarf.h"
+#include "lldb/Expression/IRExecutionUnit.h"
+#include "lldb/Expression/IRInterpreter.h"
+#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Scalar.h"
+#include "lldb/Utility/StreamString.h"
+
+#include <map>
+#include <optional>
+
+using namespace llvm;
+using lldb_private::LLDBLog;
+
+typedef SmallVector<Instruction *, 2> InstrList;
+
+IRForTarget::FunctionValueCache::FunctionValueCache(Maker const &maker)
+ : m_maker(maker), m_values() {}
+
+IRForTarget::FunctionValueCache::~FunctionValueCache() = default;
+
+llvm::Value *
+IRForTarget::FunctionValueCache::GetValue(llvm::Function *function) {
+ if (!m_values.count(function)) {
+ llvm::Value *ret = m_maker(function);
+ m_values[function] = ret;
+ return ret;
+ }
+ return m_values[function];
+}
+
+static llvm::Value *FindEntryInstruction(llvm::Function *function) {
+ if (function->empty())
+ return nullptr;
+
+ return function->getEntryBlock().getFirstNonPHIOrDbg();
+}
+
+IRForTarget::IRForTarget(lldb_private::ClangExpressionDeclMap *decl_map,
+ bool resolve_vars,
+ lldb_private::IRExecutionUnit &execution_unit,
+ lldb_private::Stream &error_stream,
+ const char *func_name)
+ : m_resolve_vars(resolve_vars), m_func_name(func_name),
+ m_decl_map(decl_map), m_error_stream(error_stream),
+ m_execution_unit(execution_unit),
+ m_entry_instruction_finder(FindEntryInstruction) {}
+
+/* Handy utility functions used at several places in the code */
+
+static std::string PrintValue(const Value *value, bool truncate = false) {
+ std::string s;
+ if (value) {
+ raw_string_ostream rso(s);
+ value->print(rso);
+ rso.flush();
+ if (truncate)
+ s.resize(s.length() - 1);
+ }
+ return s;
+}
+
+static std::string PrintType(const llvm::Type *type, bool truncate = false) {
+ std::string s;
+ raw_string_ostream rso(s);
+ type->print(rso);
+ rso.flush();
+ if (truncate)
+ s.resize(s.length() - 1);
+ return s;
+}
+
+bool IRForTarget::FixFunctionLinkage(llvm::Function &llvm_function) {
+ llvm_function.setLinkage(GlobalValue::ExternalLinkage);
+
+ return true;
+}
+
+clang::NamedDecl *IRForTarget::DeclForGlobal(const GlobalValue *global_val,
+ Module *module) {
+ NamedMDNode *named_metadata =
+ module->getNamedMetadata("clang.global.decl.ptrs");
+
+ if (!named_metadata)
+ return nullptr;
+
+ unsigned num_nodes = named_metadata->getNumOperands();
+ unsigned node_index;
+
+ for (node_index = 0; node_index < num_nodes; ++node_index) {
+ llvm::MDNode *metadata_node =
+ dyn_cast<llvm::MDNode>(named_metadata->getOperand(node_index));
+ if (!metadata_node)
+ return nullptr;
+
+ if (metadata_node->getNumOperands() != 2)
+ continue;
+
+ if (mdconst::dyn_extract_or_null<GlobalValue>(
+ metadata_node->getOperand(0)) != global_val)
+ continue;
+
+ ConstantInt *constant_int =
+ mdconst::dyn_extract<ConstantInt>(metadata_node->getOperand(1));
+
+ if (!constant_int)
+ return nullptr;
+
+ uintptr_t ptr = constant_int->getZExtValue();
+
+ return reinterpret_cast<clang::NamedDecl *>(ptr);
+ }
+
+ return nullptr;
+}
+
+clang::NamedDecl *IRForTarget::DeclForGlobal(GlobalValue *global_val) {
+ return DeclForGlobal(global_val, m_module);
+}
+
+/// Returns true iff the mangled symbol is for a static guard variable.
+static bool isGuardVariableSymbol(llvm::StringRef mangled_symbol,
+ bool check_ms_abi = true) {
+ bool result =
+ mangled_symbol.starts_with("_ZGV"); // Itanium ABI guard variable
+ if (check_ms_abi)
+ result |= mangled_symbol.ends_with("@4IA"); // Microsoft ABI
+ return result;
+}
+
+bool IRForTarget::CreateResultVariable(llvm::Function &llvm_function) {
+ lldb_private::Log *log(GetLog(LLDBLog::Expressions));
+
+ if (!m_resolve_vars)
+ return true;
+
+ // Find the result variable. If it doesn't exist, we can give up right here.
+
+ ValueSymbolTable &value_symbol_table = m_module->getValueSymbolTable();
+
+ llvm::StringRef result_name;
+ bool found_result = false;
+
+ for (StringMapEntry<llvm::Value *> &value_symbol : value_symbol_table) {
+ result_name = value_symbol.first();
+
+ // Check if this is a guard variable. It seems this causes some hiccups
+ // on Windows, so let's only check for Itanium guard variables.
+ bool is_guard_var = isGuardVariableSymbol(result_name, /*MS ABI*/ false);
+
+ if (result_name.contains("$__lldb_expr_result_ptr") && !is_guard_var) {
+ found_result = true;
+ m_result_is_pointer = true;
+ break;
+ }
+
+ if (result_name.contains("$__lldb_expr_result") && !is_guard_var) {
+ found_result = true;
+ m_result_is_pointer = false;
+ break;
+ }
+ }
+
+ if (!found_result) {
+ LLDB_LOG(log, "Couldn't find result variable");
+
+ return true;
+ }
+
+ LLDB_LOG(log, "Result name: \"{0}\"", result_name);
+
+ Value *result_value = m_module->getNamedValue(result_name);
+
+ if (!result_value) {
+ LLDB_LOG(log, "Result variable had no data");
+
+ m_error_stream.Format("Internal error [IRForTarget]: Result variable's "
+ "name ({0}) exists, but not its definition\n",
+ result_name);
+
+ return false;
+ }
+
+ LLDB_LOG(log, "Found result in the IR: \"{0}\"",
+ PrintValue(result_value, false));
+
+ GlobalVariable *result_global = dyn_cast<GlobalVariable>(result_value);
+
+ if (!result_global) {
+ LLDB_LOG(log, "Result variable isn't a GlobalVariable");
+
+ m_error_stream.Format("Internal error [IRForTarget]: Result variable ({0}) "
+ "is defined, but is not a global variable\n",
+ result_name);
+
+ return false;
+ }
+
+ clang::NamedDecl *result_decl = DeclForGlobal(result_global);
+ if (!result_decl) {
+ LLDB_LOG(log, "Result variable doesn't have a corresponding Decl");
+
+ m_error_stream.Format("Internal error [IRForTarget]: Result variable ({0}) "
+ "does not have a corresponding Clang entity\n",
+ result_name);
+
+ return false;
+ }
+
+ if (log) {
+ std::string decl_desc_str;
+ raw_string_ostream decl_desc_stream(decl_desc_str);
+ result_decl->print(decl_desc_stream);
+ decl_desc_stream.flush();
+
+ LLDB_LOG(log, "Found result decl: \"{0}\"", decl_desc_str);
+ }
+
+ clang::VarDecl *result_var = dyn_cast<clang::VarDecl>(result_decl);
+ if (!result_var) {
+ LLDB_LOG(log, "Result variable Decl isn't a VarDecl");
+
+ m_error_stream.Format("Internal error [IRForTarget]: Result variable "
+ "({0})'s corresponding Clang entity isn't a "
+ "variable\n",
+ result_name);
+
+ return false;
+ }
+
+ // Get the next available result name from m_decl_map and create the
+ // persistent variable for it
+
+ // If the result is an Lvalue, it is emitted as a pointer; see
+ // ASTResultSynthesizer::SynthesizeBodyResult.
+ if (m_result_is_pointer) {
+ clang::QualType pointer_qual_type = result_var->getType();
+ const clang::Type *pointer_type = pointer_qual_type.getTypePtr();
+
+ const clang::PointerType *pointer_pointertype =
+ pointer_type->getAs<clang::PointerType>();
+ const clang::ObjCObjectPointerType *pointer_objcobjpointertype =
+ pointer_type->getAs<clang::ObjCObjectPointerType>();
+
+ if (pointer_pointertype) {
+ clang::QualType element_qual_type = pointer_pointertype->getPointeeType();
+
+ m_result_type = lldb_private::TypeFromParser(
+ m_decl_map->GetTypeSystem()->GetType(element_qual_type));
+ } else if (pointer_objcobjpointertype) {
+ clang::QualType element_qual_type =
+ clang::QualType(pointer_objcobjpointertype->getObjectType(), 0);
+
+ m_result_type = lldb_private::TypeFromParser(
+ m_decl_map->GetTypeSystem()->GetType(element_qual_type));
+ } else {
+ LLDB_LOG(log, "Expected result to have pointer type, but it did not");
+
+ m_error_stream.Format("Internal error [IRForTarget]: Lvalue result ({0}) "
+ "is not a pointer variable\n",
+ result_name);
+
+ return false;
+ }
+ } else {
+ m_result_type = lldb_private::TypeFromParser(
+ m_decl_map->GetTypeSystem()->GetType(result_var->getType()));
+ }
+
+ lldb::TargetSP target_sp(m_execution_unit.GetTarget());
+ std::optional<uint64_t> bit_size = m_result_type.GetBitSize(target_sp.get());
+ if (!bit_size) {
+ lldb_private::StreamString type_desc_stream;
+ m_result_type.DumpTypeDescription(&type_desc_stream);
+
+ LLDB_LOG(log, "Result type has unknown size");
+
+ m_error_stream.Printf("Error [IRForTarget]: Size of result type '%s' "
+ "couldn't be determined\n",
+ type_desc_stream.GetData());
+ return false;
+ }
+
+ if (log) {
+ lldb_private::StreamString type_desc_stream;
+ m_result_type.DumpTypeDescription(&type_desc_stream);
+
+ LLDB_LOG(log, "Result decl type: \"{0}\"", type_desc_stream.GetData());
+ }
+
+ m_result_name = lldb_private::ConstString("$RESULT_NAME");
+
+ LLDB_LOG(log, "Creating a new result global: \"{0}\" with size {1}",
+ m_result_name,
+ m_result_type.GetByteSize(target_sp.get()).value_or(0));
+
+ // Construct a new result global and set up its metadata
+
+ GlobalVariable *new_result_global = new GlobalVariable(
+ (*m_module), result_global->getValueType(), false, /* not constant */
+ GlobalValue::ExternalLinkage, nullptr, /* no initializer */
+ m_result_name.GetCString());
+
+ // It's too late in compilation to create a new VarDecl for this, but we
+ // don't need to. We point the metadata at the old VarDecl. This creates an
+ // odd anomaly: a variable with a Value whose name is something like $0 and a
+ // Decl whose name is $__lldb_expr_result. This condition is handled in
+ // ClangExpressionDeclMap::DoMaterialize, and the name of the variable is
+ // fixed up.
+
+ ConstantInt *new_constant_int =
+ ConstantInt::get(llvm::Type::getInt64Ty(m_module->getContext()),
+ reinterpret_cast<uintptr_t>(result_decl), false);
+
+ llvm::Metadata *values[2];
+ values[0] = ConstantAsMetadata::get(new_result_global);
+ values[1] = ConstantAsMetadata::get(new_constant_int);
+
+ ArrayRef<Metadata *> value_ref(values, 2);
+
+ MDNode *persistent_global_md = MDNode::get(m_module->getContext(), value_ref);
+ NamedMDNode *named_metadata =
+ m_module->getNamedMetadata("clang.global.decl.ptrs");
+ named_metadata->addOperand(persistent_global_md);
+
+ LLDB_LOG(log, "Replacing \"{0}\" with \"{1}\"", PrintValue(result_global),
+ PrintValue(new_result_global));
+
+ if (result_global->use_empty()) {
+ // We need to synthesize a store for this variable, because otherwise
+ // there's nothing to put into its equivalent persistent variable.
+
+ BasicBlock &entry_block(llvm_function.getEntryBlock());
+ Instruction *first_entry_instruction(entry_block.getFirstNonPHIOrDbg());
+
+ if (!first_entry_instruction)
+ return false;
+
+ if (!result_global->hasInitializer()) {
+ LLDB_LOG(log, "Couldn't find initializer for unused variable");
+
+ m_error_stream.Format("Internal error [IRForTarget]: Result variable "
+ "({0}) has no writes and no initializer\n",
+ result_name);
+
+ return false;
+ }
+
+ Constant *initializer = result_global->getInitializer();
+
+ StoreInst *synthesized_store =
+ new StoreInst(initializer, new_result_global, first_entry_instruction);
+
+ LLDB_LOG(log, "Synthesized result store \"{0}\"\n",
+ PrintValue(synthesized_store));
+ } else {
+ result_global->replaceAllUsesWith(new_result_global);
+ }
+
+ if (!m_decl_map->AddPersistentVariable(
+ result_decl, m_result_name, m_result_type, true, m_result_is_pointer))
+ return false;
+
+ result_global->eraseFromParent();
+
+ return true;
+}
+
+bool IRForTarget::RewriteObjCConstString(llvm::GlobalVariable *ns_str,
+ llvm::GlobalVariable *cstr) {
+ lldb_private::Log *log(GetLog(LLDBLog::Expressions));
+
+ Type *ns_str_ty = ns_str->getType();
+
+ Type *i8_ptr_ty = PointerType::getUnqual(m_module->getContext());
+ Type *i32_ty = Type::getInt32Ty(m_module->getContext());
+ Type *i8_ty = Type::getInt8Ty(m_module->getContext());
+
+ if (!m_CFStringCreateWithBytes) {
+ lldb::addr_t CFStringCreateWithBytes_addr;
+
+ static lldb_private::ConstString g_CFStringCreateWithBytes_str(
+ "CFStringCreateWithBytes");
+
+ bool missing_weak = false;
+ CFStringCreateWithBytes_addr =
+ m_execution_unit.FindSymbol(g_CFStringCreateWithBytes_str,
+ missing_weak);
+ if (CFStringCreateWithBytes_addr == LLDB_INVALID_ADDRESS || missing_weak) {
+ LLDB_LOG(log, "Couldn't find CFStringCreateWithBytes in the target");
+
+ m_error_stream.Printf("Error [IRForTarget]: Rewriting an Objective-C "
+ "constant string requires "
+ "CFStringCreateWithBytes\n");
+
+ return false;
+ }
+
+ LLDB_LOG(log, "Found CFStringCreateWithBytes at {0}",
+ CFStringCreateWithBytes_addr);
+
+ // Build the function type:
+ //
+ // CFStringRef CFStringCreateWithBytes (
+ // CFAllocatorRef alloc,
+ // const UInt8 *bytes,
+ // CFIndex numBytes,
+ // CFStringEncoding encoding,
+ // Boolean isExternalRepresentation
+ // );
+ //
+ // We make the following substitutions:
+ //
+ // CFStringRef -> i8*
+ // CFAllocatorRef -> i8*
+ // UInt8 * -> i8*
+ // CFIndex -> long (i32 or i64, as appropriate; we ask the module for its
+ // pointer size for now) CFStringEncoding -> i32 Boolean -> i8
+
+ Type *arg_type_array[5];
+
+ arg_type_array[0] = i8_ptr_ty;
+ arg_type_array[1] = i8_ptr_ty;
+ arg_type_array[2] = m_intptr_ty;
+ arg_type_array[3] = i32_ty;
+ arg_type_array[4] = i8_ty;
+
+ ArrayRef<Type *> CFSCWB_arg_types(arg_type_array, 5);
+
+ llvm::FunctionType *CFSCWB_ty =
+ FunctionType::get(ns_str_ty, CFSCWB_arg_types, false);
+
+ // Build the constant containing the pointer to the function
+ PointerType *CFSCWB_ptr_ty = PointerType::getUnqual(CFSCWB_ty);
+ Constant *CFSCWB_addr_int =
+ ConstantInt::get(m_intptr_ty, CFStringCreateWithBytes_addr, false);
+ m_CFStringCreateWithBytes = {
+ CFSCWB_ty, ConstantExpr::getIntToPtr(CFSCWB_addr_int, CFSCWB_ptr_ty)};
+ }
+
+ ConstantDataSequential *string_array = nullptr;
+
+ if (cstr)
+ string_array = dyn_cast<ConstantDataSequential>(cstr->getInitializer());
+
+ Constant *alloc_arg = Constant::getNullValue(i8_ptr_ty);
+ Constant *bytes_arg = cstr ? cstr : Constant::getNullValue(i8_ptr_ty);
+ Constant *numBytes_arg = ConstantInt::get(
+ m_intptr_ty, cstr ? (string_array->getNumElements() - 1) * string_array->getElementByteSize() : 0, false);
+ int encoding_flags = 0;
+ switch (cstr ? string_array->getElementByteSize() : 1) {
+ case 1:
+ encoding_flags = 0x08000100; /* 0x08000100 is kCFStringEncodingUTF8 */
+ break;
+ case 2:
+ encoding_flags = 0x0100; /* 0x0100 is kCFStringEncodingUTF16 */
+ break;
+ case 4:
+ encoding_flags = 0x0c000100; /* 0x0c000100 is kCFStringEncodingUTF32 */
+ break;
+ default:
+ encoding_flags = 0x0600; /* fall back to 0x0600, kCFStringEncodingASCII */
+ LLDB_LOG(log, "Encountered an Objective-C constant string with unusual "
+ "element size {0}",
+ string_array->getElementByteSize());
+ }
+ Constant *encoding_arg = ConstantInt::get(i32_ty, encoding_flags, false);
+ Constant *isExternal_arg =
+ ConstantInt::get(i8_ty, 0x0, false); /* 0x0 is false */
+
+ Value *argument_array[5];
+
+ argument_array[0] = alloc_arg;
+ argument_array[1] = bytes_arg;
+ argument_array[2] = numBytes_arg;
+ argument_array[3] = encoding_arg;
+ argument_array[4] = isExternal_arg;
+
+ ArrayRef<Value *> CFSCWB_arguments(argument_array, 5);
+
+ FunctionValueCache CFSCWB_Caller(
+ [this, &CFSCWB_arguments](llvm::Function *function) -> llvm::Value * {
+ return CallInst::Create(
+ m_CFStringCreateWithBytes, CFSCWB_arguments,
+ "CFStringCreateWithBytes",
+ llvm::cast<Instruction>(
+ m_entry_instruction_finder.GetValue(function)));
+ });
+
+ if (!UnfoldConstant(ns_str, nullptr, CFSCWB_Caller, m_entry_instruction_finder,
+ m_error_stream)) {
+ LLDB_LOG(log, "Couldn't replace the NSString with the result of the call");
+
+ m_error_stream.Printf("error [IRForTarget internal]: Couldn't replace an "
+ "Objective-C constant string with a dynamic "
+ "string\n");
+
+ return false;
+ }
+
+ ns_str->eraseFromParent();
+
+ return true;
+}
+
+bool IRForTarget::RewriteObjCConstStrings() {
+ lldb_private::Log *log(GetLog(LLDBLog::Expressions));
+
+ ValueSymbolTable &value_symbol_table = m_module->getValueSymbolTable();
+
+ for (StringMapEntry<llvm::Value *> &value_symbol : value_symbol_table) {
+ llvm::StringRef value_name = value_symbol.first();
+
+ if (value_name.contains("_unnamed_cfstring_")) {
+ Value *nsstring_value = value_symbol.second;
+
+ GlobalVariable *nsstring_global =
+ dyn_cast<GlobalVariable>(nsstring_value);
+
+ if (!nsstring_global) {
+ LLDB_LOG(log, "NSString variable is not a GlobalVariable");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
+ "constant string is not a global variable\n");
+
+ return false;
+ }
+
+ if (!nsstring_global->hasInitializer()) {
+ LLDB_LOG(log, "NSString variable does not have an initializer");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
+ "constant string does not have an initializer\n");
+
+ return false;
+ }
+
+ ConstantStruct *nsstring_struct =
+ dyn_cast<ConstantStruct>(nsstring_global->getInitializer());
+
+ if (!nsstring_struct) {
+ LLDB_LOG(log,
+ "NSString variable's initializer is not a ConstantStruct");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
+ "constant string is not a structure constant\n");
+
+ return false;
+ }
+
+ // We expect the following structure:
+ //
+ // struct {
+ // int *isa;
+ // int flags;
+ // char *str;
+ // long length;
+ // };
+
+ if (nsstring_struct->getNumOperands() != 4) {
+
+ LLDB_LOG(log,
+ "NSString variable's initializer structure has an "
+ "unexpected number of members. Should be 4, is {0}",
+ nsstring_struct->getNumOperands());
+
+ m_error_stream.Printf("Internal error [IRForTarget]: The struct for an "
+ "Objective-C constant string is not as "
+ "expected\n");
+
+ return false;
+ }
+
+ Constant *nsstring_member = nsstring_struct->getOperand(2);
+
+ if (!nsstring_member) {
+ LLDB_LOG(log, "NSString initializer's str element was empty");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
+ "constant string does not have a string "
+ "initializer\n");
+
+ return false;
+ }
+
+ auto *cstr_global = dyn_cast<GlobalVariable>(nsstring_member);
+ if (!cstr_global) {
+ LLDB_LOG(log,
+ "NSString initializer's str element is not a GlobalVariable");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Unhandled"
+ "constant string initializer\n");
+
+ return false;
+ }
+
+ if (!cstr_global->hasInitializer()) {
+ LLDB_LOG(log, "NSString initializer's str element does not have an "
+ "initializer");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
+ "constant string's string initializer doesn't "
+ "point to initialized data\n");
+
+ return false;
+ }
+
+ /*
+ if (!cstr_array)
+ {
+ if (log)
+ log->PutCString("NSString initializer's str element is not a
+ ConstantArray");
+
+ if (m_error_stream)
+ m_error_stream.Printf("Internal error [IRForTarget]: An
+ Objective-C constant string's string initializer doesn't point to an
+ array\n");
+
+ return false;
+ }
+
+ if (!cstr_array->isCString())
+ {
+ if (log)
+ log->PutCString("NSString initializer's str element is not a C
+ string array");
+
+ if (m_error_stream)
+ m_error_stream.Printf("Internal error [IRForTarget]: An
+ Objective-C constant string's string initializer doesn't point to a C
+ string\n");
+
+ return false;
+ }
+ */
+
+ ConstantDataArray *cstr_array =
+ dyn_cast<ConstantDataArray>(cstr_global->getInitializer());
+
+ if (cstr_array)
+ LLDB_LOG(log, "Found NSString constant {0}, which contains \"{1}\"",
+ value_name, cstr_array->getAsString());
+ else
+ LLDB_LOG(log, "Found NSString constant {0}, which contains \"\"",
+ value_name);
+
+ if (!cstr_array)
+ cstr_global = nullptr;
+
+ if (!RewriteObjCConstString(nsstring_global, cstr_global)) {
+ LLDB_LOG(log, "Error rewriting the constant string");
+
+ // We don't print an error message here because RewriteObjCConstString
+ // has done so for us.
+
+ return false;
+ }
+ }
+ }
+
+ for (StringMapEntry<llvm::Value *> &value_symbol : value_symbol_table) {
+ llvm::StringRef value_name = value_symbol.first();
+
+ if (value_name == "__CFConstantStringClassReference") {
+ GlobalVariable *gv = dyn_cast<GlobalVariable>(value_symbol.second);
+
+ if (!gv) {
+ LLDB_LOG(log,
+ "__CFConstantStringClassReference is not a global variable");
+
+ m_error_stream.Printf("Internal error [IRForTarget]: Found a "
+ "CFConstantStringClassReference, but it is not a "
+ "global object\n");
+
+ return false;
+ }
+
+ gv->eraseFromParent();
+
+ break;
+ }
+ }
+
+ return true;
+}
+
+static bool IsObjCSelectorRef(Value *value) {
+ GlobalVariable *global_variable = dyn_cast<GlobalVariable>(value);
+
+ return !(
+ !global_variable || !global_variable->hasName() ||
+ !global_variable->getName().starts_with("OBJC_SELECTOR_REFERENCES_"));
+}
+
+// This function does not report errors; its callers are responsible.
+bool IRForTarget::RewriteObjCSelector(Instruction *selector_load) {
+ lldb_private::Log *log(GetLog(LLDBLog::Expressions));
+
+ LoadInst *load = dyn_cast<LoadInst>(selector_load);
+
+ if (!load)
+ return false;
+
+ // Unpack the message name from the selector. In LLVM IR, an objc_msgSend
+ // gets represented as
+ //
+ // %sel = load ptr, ptr @OBJC_SELECTOR_REFERENCES_, align 8
+ // call i8 @objc_msgSend(ptr %obj, ptr %sel, ...)
+ //
+ // where %obj is the object pointer and %sel is the selector.
+ //
+ // @"OBJC_SELECTOR_REFERENCES_" is a pointer to a character array called
+ // @"\01L_OBJC_METH_VAR_NAME_".
+ // @"\01L_OBJC_METH_VAR_NAME_" contains the string.
+
+ // Find the pointer's initializer and get the string from its target.
+
+ GlobalVariable *_objc_selector_references_ =
+ dyn_cast<GlobalVariable>(load->getPointerOperand());
+
+ if (!_objc_selector_references_ ||
+ !_objc_selector_references_->hasInitializer())
+ return false;
+
+ Constant *osr_initializer = _objc_selector_references_->getInitializer();
+ if (!osr_initializer)
+ return false;
+
+ // Find the string's initializer (a ConstantArray) and get the string from it
+
+ GlobalVariable *_objc_meth_var_name_ =
+ dyn_cast<GlobalVariable>(osr_initializer);
+
+ if (!_objc_meth_var_name_ || !_objc_meth_var_name_->hasInitializer())
+ return false;
+
+ Constant *omvn_initializer = _objc_meth_var_name_->getInitializer();
+
+ ConstantDataArray *omvn_initializer_array =
+ dyn_cast<ConstantDataArray>(omvn_initializer);
+
+ if (!omvn_initializer_array->isString())
+ return false;
+
+ std::string omvn_initializer_string =
+ std::string(omvn_initializer_array->getAsString());
+
+ LLDB_LOG(log, "Found Objective-C selector reference \"{0}\"",
+ omvn_initializer_string);
+
+ // Construct a call to sel_registerName
+
+ if (!m_sel_registerName) {
+ lldb::addr_t sel_registerName_addr;
+
+ bool missing_weak = false;
+ static lldb_private::ConstString g_sel_registerName_str("sel_registerName");
+ sel_registerName_addr = m_execution_unit.FindSymbol(g_sel_registerName_str,
+ missing_weak);
+ if (sel_registerName_addr == LLDB_INVALID_ADDRESS || missing_weak)
+ return false;
+
+ LLDB_LOG(log, "Found sel_registerName at {0}", sel_registerName_addr);
+
+ // Build the function type: struct objc_selector
+ // *sel_registerName(uint8_t*)
+
+ // The below code would be "more correct," but in actuality what's required
+ // is uint8_t*
+ // Type *sel_type = StructType::get(m_module->getContext());
+ // Type *sel_ptr_type = PointerType::getUnqual(sel_type);
+ Type *sel_ptr_type = PointerType::getUnqual(m_module->getContext());
+
+ Type *type_array[1];
+
+ type_array[0] = llvm::PointerType::getUnqual(m_module->getContext());
+
+ ArrayRef<Type *> srN_arg_types(type_array, 1);
+
+ llvm::FunctionType *srN_type =
+ FunctionType::get(sel_ptr_type, srN_arg_types, false);
+
+ // Build the constant containing the pointer to the function
+ PointerType *srN_ptr_ty = PointerType::getUnqual(srN_type);
+ Constant *srN_addr_int =
+ ConstantInt::get(m_intptr_ty, sel_registerName_addr, false);
+ m_sel_registerName = {srN_type,
+ ConstantExpr::getIntToPtr(srN_addr_int, srN_ptr_ty)};
+ }
+
+ CallInst *srN_call =
+ CallInst::Create(m_sel_registerName, _objc_meth_var_name_,
+ "sel_registerName", selector_load);
+
+ // Replace the load with the call in all users
+
+ selector_load->replaceAllUsesWith(srN_call);
+
+ selector_load->eraseFromParent();
+
+ return true;
+}
+
+bool IRForTarget::RewriteObjCSelectors(BasicBlock &basic_block) {
+ lldb_private::Log *log(GetLog(LLDBLog::Expressions));
+
+ InstrList selector_loads;
+
+ for (Instruction &inst : basic_block) {
+ if (LoadInst *load = dyn_cast<LoadInst>(&inst))
+ if (IsObjCSelectorRef(load->getPointerOperand()))
+ selector_loads.push_back(&inst);
+ }
+
+ for (Instruction *inst : selector_loads) {
+ if (!RewriteObjCSelector(inst)) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't change a "
+ "static reference to an Objective-C selector to a "
+ "dynamic reference\n");
+
+ LLDB_LOG(log, "Couldn't rewrite a reference to an Objective-C selector");
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// This function does not report errors; its callers are responsible.
+bool IRForTarget::RewritePersistentAlloc(llvm::Instruction *persistent_alloc) {
+ lldb_private::Log *log(GetLog(LLDBLog::Expressions));
+
+ AllocaInst *alloc = dyn_cast<AllocaInst>(persistent_alloc);
+
+ MDNode *alloc_md = alloc->getMetadata("clang.decl.ptr");
+
+ if (!alloc_md || !alloc_md->getNumOperands())
+ return false;
+
+ ConstantInt *constant_int =
+ mdconst::dyn_extract<ConstantInt>(alloc_md->getOperand(0));
+
+ if (!constant_int)
+ return false;
+
+ // We attempt to register this as a new persistent variable with the DeclMap.
+
+ uintptr_t ptr = constant_int->getZExtValue();
+
+ clang::VarDecl *decl = reinterpret_cast<clang::VarDecl *>(ptr);
+
+ lldb_private::TypeFromParser result_decl_type(
+ m_decl_map->GetTypeSystem()->GetType(decl->getType()));
+
+ StringRef decl_name(decl->getName());
+ lldb_private::ConstString persistent_variable_name(decl_name.data(),
+ decl_name.size());
+ if (!m_decl_map->AddPersistentVariable(decl, persistent_variable_name,
+ result_decl_type, false, false))
+ return false;
+
+ GlobalVariable *persistent_global = new GlobalVariable(
+ (*m_module), alloc->getType(), false, /* not constant */
+ GlobalValue::ExternalLinkage, nullptr, /* no initializer */
+ alloc->getName().str());
+
+ // What we're going to do here is make believe this was a regular old
+ // external variable. That means we need to make the metadata valid.
+
+ NamedMDNode *named_metadata =
+ m_module->getOrInsertNamedMetadata("clang.global.decl.ptrs");
+
+ llvm::Metadata *values[2];
+ values[0] = ConstantAsMetadata::get(persistent_global);
+ values[1] = ConstantAsMetadata::get(constant_int);
+
+ ArrayRef<llvm::Metadata *> value_ref(values, 2);
+
+ MDNode *persistent_global_md = MDNode::get(m_module->getContext(), value_ref);
+ named_metadata->addOperand(persistent_global_md);
+
+ // Now, since the variable is a pointer variable, we will drop in a load of
+ // that pointer variable.
+
+ LoadInst *persistent_load = new LoadInst(persistent_global->getValueType(),
+ persistent_global, "", alloc);
+
+ LLDB_LOG(log, "Replacing \"{0}\" with \"{1}\"", PrintValue(alloc),
+ PrintValue(persistent_load));
+
+ alloc->replaceAllUsesWith(persistent_load);
+ alloc->eraseFromParent();
+
+ return true;
+}
+
+bool IRForTarget::RewritePersistentAllocs(llvm::BasicBlock &basic_block) {
+ if (!m_resolve_vars)
+ return true;
+
+ lldb_private::Log *log(GetLog(LLDBLog::Expressions));
+
+ InstrList pvar_allocs;
+
+ for (Instruction &inst : basic_block) {
+
+ if (AllocaInst *alloc = dyn_cast<AllocaInst>(&inst)) {
+ llvm::StringRef alloc_name = alloc->getName();
+
+ if (alloc_name.starts_with("$") && !alloc_name.starts_with("$__lldb")) {
+ if (alloc_name.find_first_of("0123456789") == 1) {
+ LLDB_LOG(log, "Rejecting a numeric persistent variable.");
+
+ m_error_stream.Printf("Error [IRForTarget]: Names starting with $0, "
+ "$1, ... are reserved for use as result "
+ "names\n");
+
+ return false;
+ }
+
+ pvar_allocs.push_back(alloc);
+ }
+ }
+ }
+
+ for (Instruction *inst : pvar_allocs) {
+ if (!RewritePersistentAlloc(inst)) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't rewrite "
+ "the creation of a persistent variable\n");
+
+ LLDB_LOG(log, "Couldn't rewrite the creation of a persistent variable");
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// This function does not report errors; its callers are responsible.
+bool IRForTarget::MaybeHandleVariable(Value *llvm_value_ptr) {
+ lldb_private::Log *log(GetLog(LLDBLog::Expressions));
+
+ LLDB_LOG(log, "MaybeHandleVariable ({0})", PrintValue(llvm_value_ptr));
+
+ if (ConstantExpr *constant_expr = dyn_cast<ConstantExpr>(llvm_value_ptr)) {
+ switch (constant_expr->getOpcode()) {
+ default:
+ break;
+ case Instruction::GetElementPtr:
+ case Instruction::BitCast:
+ Value *s = constant_expr->getOperand(0);
+ if (!MaybeHandleVariable(s))
+ return false;
+ }
+ } else if (GlobalVariable *global_variable =
+ dyn_cast<GlobalVariable>(llvm_value_ptr)) {
+ if (!GlobalValue::isExternalLinkage(global_variable->getLinkage()))
+ return true;
+
+ clang::NamedDecl *named_decl = DeclForGlobal(global_variable);
+
+ if (!named_decl) {
+ if (IsObjCSelectorRef(llvm_value_ptr))
+ return true;
+
+ if (!global_variable->hasExternalLinkage())
+ return true;
+
+ LLDB_LOG(log, "Found global variable \"{0}\" without metadata",
+ global_variable->getName());
+
+ return false;
+ }
+
+ llvm::StringRef name(named_decl->getName());
+
+ clang::ValueDecl *value_decl = dyn_cast<clang::ValueDecl>(named_decl);
+ if (value_decl == nullptr)
+ return false;
+
+ lldb_private::CompilerType compiler_type =
+ m_decl_map->GetTypeSystem()->GetType(value_decl->getType());
+
+ const Type *value_type = nullptr;
+
+ if (name.starts_with("$")) {
+ // The $__lldb_expr_result name indicates the return value has allocated
+ // as a static variable. Per the comment at
+ // ASTResultSynthesizer::SynthesizeBodyResult, accesses to this static
+ // variable need to be redirected to the result of dereferencing a
+ // pointer that is passed in as one of the arguments.
+ //
+ // Consequently, when reporting the size of the type, we report a pointer
+ // type pointing to the type of $__lldb_expr_result, not the type itself.
+ //
+ // We also do this for any user-declared persistent variables.
+ compiler_type = compiler_type.GetPointerType();
+ value_type = PointerType::get(global_variable->getType(), 0);
+ } else {
+ value_type = global_variable->getType();
+ }
+
+ auto *target = m_execution_unit.GetTarget().get();
+ std::optional<uint64_t> value_size = compiler_type.GetByteSize(target);
+ if (!value_size)
+ return false;
+ std::optional<size_t> opt_alignment = compiler_type.GetTypeBitAlign(target);
+ if (!opt_alignment)
+ return false;
+ lldb::offset_t value_alignment = (*opt_alignment + 7ull) / 8ull;
+
+ LLDB_LOG(log,
+ "Type of \"{0}\" is [clang \"{1}\", llvm \"{2}\"] [size {3}, "
+ "align {4}]",
+ name,
+ lldb_private::ClangUtil::GetQualType(compiler_type).getAsString(),
+ PrintType(value_type), *value_size, value_alignment);
+
+ if (named_decl)
+ m_decl_map->AddValueToStruct(named_decl, lldb_private::ConstString(name),
+ llvm_value_ptr, *value_size,
+ value_alignment);
+ } else if (isa<llvm::Function>(llvm_value_ptr)) {
+ LLDB_LOG(log, "Function pointers aren't handled right now");
+
+ return false;
+ }
+
+ return true;
+}
+
+// This function does not report errors; its callers are responsible.
+bool IRForTarget::HandleSymbol(Value *symbol) {
+ lldb_private::Log *log(GetLog(LLDBLog::Expressions));
+
+ lldb_private::ConstString name(symbol->getName().str().c_str());
+
+ lldb::addr_t symbol_addr =
+ m_decl_map->GetSymbolAddress(name, lldb::eSymbolTypeAny);
+
+ if (symbol_addr == LLDB_INVALID_ADDRESS) {
+ LLDB_LOG(log, "Symbol \"{0}\" had no address", name);
+
+ return false;
+ }
+
+ LLDB_LOG(log, "Found \"{0}\" at {1}", name, symbol_addr);
+
+ Type *symbol_type = symbol->getType();
+
+ Constant *symbol_addr_int = ConstantInt::get(m_intptr_ty, symbol_addr, false);
+
+ Value *symbol_addr_ptr =
+ ConstantExpr::getIntToPtr(symbol_addr_int, symbol_type);
+
+ LLDB_LOG(log, "Replacing {0} with {1}", PrintValue(symbol),
+ PrintValue(symbol_addr_ptr));
+
+ symbol->replaceAllUsesWith(symbol_addr_ptr);
+
+ return true;
+}
+
+bool IRForTarget::MaybeHandleCallArguments(CallInst *Old) {
+ lldb_private::Log *log(GetLog(LLDBLog::Expressions));
+
+ LLDB_LOG(log, "MaybeHandleCallArguments({0})", PrintValue(Old));
+
+ for (unsigned op_index = 0, num_ops = Old->arg_size();
+ op_index < num_ops; ++op_index)
+ // conservatively believe that this is a store
+ if (!MaybeHandleVariable(Old->getArgOperand(op_index))) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't rewrite "
+ "one of the arguments of a function call.\n");
+
+ return false;
+ }
+
+ return true;
+}
+
+bool IRForTarget::HandleObjCClass(Value *classlist_reference) {
+ lldb_private::Log *log(GetLog(LLDBLog::Expressions));
+
+ GlobalVariable *global_variable =
+ dyn_cast<GlobalVariable>(classlist_reference);
+
+ if (!global_variable)
+ return false;
+
+ Constant *initializer = global_variable->getInitializer();
+
+ if (!initializer)
+ return false;
+
+ if (!initializer->hasName())
+ return false;
+
+ StringRef name(initializer->getName());
+ lldb_private::ConstString name_cstr(name.str().c_str());
+ lldb::addr_t class_ptr =
+ m_decl_map->GetSymbolAddress(name_cstr, lldb::eSymbolTypeObjCClass);
+
+ LLDB_LOG(log, "Found reference to Objective-C class {0} ({1})", name,
+ (unsigned long long)class_ptr);
+
+ if (class_ptr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ if (global_variable->use_empty())
+ return false;
+
+ SmallVector<LoadInst *, 2> load_instructions;
+
+ for (llvm::User *u : global_variable->users()) {
+ if (LoadInst *load_instruction = dyn_cast<LoadInst>(u))
+ load_instructions.push_back(load_instruction);
+ }
+
+ if (load_instructions.empty())
+ return false;
+
+ Constant *class_addr = ConstantInt::get(m_intptr_ty, (uint64_t)class_ptr);
+
+ for (LoadInst *load_instruction : load_instructions) {
+ Constant *class_bitcast =
+ ConstantExpr::getIntToPtr(class_addr, load_instruction->getType());
+
+ load_instruction->replaceAllUsesWith(class_bitcast);
+
+ load_instruction->eraseFromParent();
+ }
+
+ return true;
+}
+
+bool IRForTarget::RemoveCXAAtExit(BasicBlock &basic_block) {
+ std::vector<CallInst *> calls_to_remove;
+
+ for (Instruction &inst : basic_block) {
+ CallInst *call = dyn_cast<CallInst>(&inst);
+
+ // MaybeHandleCallArguments handles error reporting; we are silent here
+ if (!call)
+ continue;
+
+ bool remove = false;
+
+ llvm::Function *func = call->getCalledFunction();
+
+ if (func && func->getName() == "__cxa_atexit")
+ remove = true;
+
+ llvm::Value *val = call->getCalledOperand();
+
+ if (val && val->getName() == "__cxa_atexit")
+ remove = true;
+
+ if (remove)
+ calls_to_remove.push_back(call);
+ }
+
+ for (CallInst *ci : calls_to_remove)
+ ci->eraseFromParent();
+
+ return true;
+}
+
+bool IRForTarget::ResolveCalls(BasicBlock &basic_block) {
+ // Prepare the current basic block for execution in the remote process
+
+ for (Instruction &inst : basic_block) {
+ CallInst *call = dyn_cast<CallInst>(&inst);
+
+ // MaybeHandleCallArguments handles error reporting; we are silent here
+ if (call && !MaybeHandleCallArguments(call))
+ return false;
+ }
+
+ return true;
+}
+
+bool IRForTarget::ResolveExternals(Function &llvm_function) {
+ lldb_private::Log *log(GetLog(LLDBLog::Expressions));
+
+ for (GlobalVariable &global_var : m_module->globals()) {
+ llvm::StringRef global_name = global_var.getName();
+
+ LLDB_LOG(log, "Examining {0}, DeclForGlobalValue returns {1}", global_name,
+ static_cast<void *>(DeclForGlobal(&global_var)));
+
+ if (global_name.starts_with("OBJC_IVAR")) {
+ if (!HandleSymbol(&global_var)) {
+ m_error_stream.Format("Error [IRForTarget]: Couldn't find Objective-C "
+ "indirect ivar symbol {0}\n",
+ global_name);
+
+ return false;
+ }
+ } else if (global_name.contains("OBJC_CLASSLIST_REFERENCES_$")) {
+ if (!HandleObjCClass(&global_var)) {
+ m_error_stream.Printf("Error [IRForTarget]: Couldn't resolve the class "
+ "for an Objective-C static method call\n");
+
+ return false;
+ }
+ } else if (global_name.contains("OBJC_CLASSLIST_SUP_REFS_$")) {
+ if (!HandleObjCClass(&global_var)) {
+ m_error_stream.Printf("Error [IRForTarget]: Couldn't resolve the class "
+ "for an Objective-C static method call\n");
+
+ return false;
+ }
+ } else if (DeclForGlobal(&global_var)) {
+ if (!MaybeHandleVariable(&global_var)) {
+ m_error_stream.Format("Internal error [IRForTarget]: Couldn't rewrite "
+ "external variable {0}\n",
+ global_name);
+
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool isGuardVariableRef(Value *V) {
+ GlobalVariable *GV = dyn_cast<GlobalVariable>(V);
+
+ if (!GV || !GV->hasName() || !isGuardVariableSymbol(GV->getName()))
+ return false;
+
+ return true;
+}
+
+void IRForTarget::TurnGuardLoadIntoZero(llvm::Instruction *guard_load) {
+ Constant *zero(Constant::getNullValue(guard_load->getType()));
+ guard_load->replaceAllUsesWith(zero);
+ guard_load->eraseFromParent();
+}
+
+static void ExciseGuardStore(Instruction *guard_store) {
+ guard_store->eraseFromParent();
+}
+
+bool IRForTarget::RemoveGuards(BasicBlock &basic_block) {
+ // Eliminate any reference to guard variables found.
+
+ InstrList guard_loads;
+ InstrList guard_stores;
+
+ for (Instruction &inst : basic_block) {
+
+ if (LoadInst *load = dyn_cast<LoadInst>(&inst))
+ if (isGuardVariableRef(load->getPointerOperand()))
+ guard_loads.push_back(&inst);
+
+ if (StoreInst *store = dyn_cast<StoreInst>(&inst))
+ if (isGuardVariableRef(store->getPointerOperand()))
+ guard_stores.push_back(&inst);
+ }
+
+ for (Instruction *inst : guard_loads)
+ TurnGuardLoadIntoZero(inst);
+
+ for (Instruction *inst : guard_stores)
+ ExciseGuardStore(inst);
+
+ return true;
+}
+
+// This function does not report errors; its callers are responsible.
+bool IRForTarget::UnfoldConstant(Constant *old_constant,
+ llvm::Function *llvm_function,
+ FunctionValueCache &value_maker,
+ FunctionValueCache &entry_instruction_finder,
+ lldb_private::Stream &error_stream) {
+ SmallVector<User *, 16> users;
+
+ // We do this because the use list might change, invalidating our iterator.
+ // Much better to keep a work list ourselves.
+ for (llvm::User *u : old_constant->users())
+ users.push_back(u);
+
+ for (size_t i = 0; i < users.size(); ++i) {
+ User *user = users[i];
+
+ if (Constant *constant = dyn_cast<Constant>(user)) {
+ // synthesize a new non-constant equivalent of the constant
+
+ if (ConstantExpr *constant_expr = dyn_cast<ConstantExpr>(constant)) {
+ switch (constant_expr->getOpcode()) {
+ default:
+ error_stream.Printf("error [IRForTarget internal]: Unhandled "
+ "constant expression type: \"%s\"",
+ PrintValue(constant_expr).c_str());
+ return false;
+ case Instruction::BitCast: {
+ FunctionValueCache bit_cast_maker(
+ [&value_maker, &entry_instruction_finder, old_constant,
+ constant_expr](llvm::Function *function) -> llvm::Value * {
+ // UnaryExpr
+ // OperandList[0] is value
+
+ if (constant_expr->getOperand(0) != old_constant)
+ return constant_expr;
+
+ return new BitCastInst(
+ value_maker.GetValue(function), constant_expr->getType(),
+ "", llvm::cast<Instruction>(
+ entry_instruction_finder.GetValue(function)));
+ });
+
+ if (!UnfoldConstant(constant_expr, llvm_function, bit_cast_maker,
+ entry_instruction_finder, error_stream))
+ return false;
+ } break;
+ case Instruction::GetElementPtr: {
+ // GetElementPtrConstantExpr
+ // OperandList[0] is base
+ // OperandList[1]... are indices
+
+ FunctionValueCache get_element_pointer_maker(
+ [&value_maker, &entry_instruction_finder, old_constant,
+ constant_expr](llvm::Function *function) -> llvm::Value * {
+ auto *gep = cast<llvm::GEPOperator>(constant_expr);
+ Value *ptr = gep->getPointerOperand();
+
+ if (ptr == old_constant)
+ ptr = value_maker.GetValue(function);
+
+ std::vector<Value *> index_vector;
+ for (Value *operand : gep->indices()) {
+ if (operand == old_constant)
+ operand = value_maker.GetValue(function);
+
+ index_vector.push_back(operand);
+ }
+
+ ArrayRef<Value *> indices(index_vector);
+
+ return GetElementPtrInst::Create(
+ gep->getSourceElementType(), ptr, indices, "",
+ llvm::cast<Instruction>(
+ entry_instruction_finder.GetValue(function)));
+ });
+
+ if (!UnfoldConstant(constant_expr, llvm_function,
+ get_element_pointer_maker,
+ entry_instruction_finder, error_stream))
+ return false;
+ } break;
+ }
+ } else {
+ error_stream.Printf(
+ "error [IRForTarget internal]: Unhandled constant type: \"%s\"",
+ PrintValue(constant).c_str());
+ return false;
+ }
+ } else {
+ if (Instruction *inst = llvm::dyn_cast<Instruction>(user)) {
+ if (llvm_function && inst->getParent()->getParent() != llvm_function) {
+ error_stream.PutCString("error: Capturing non-local variables in "
+ "expressions is unsupported.\n");
+ return false;
+ }
+ inst->replaceUsesOfWith(
+ old_constant, value_maker.GetValue(inst->getParent()->getParent()));
+ } else {
+ error_stream.Printf(
+ "error [IRForTarget internal]: Unhandled non-constant type: \"%s\"",
+ PrintValue(user).c_str());
+ return false;
+ }
+ }
+ }
+
+ if (!isa<GlobalValue>(old_constant)) {
+ old_constant->destroyConstant();
+ }
+
+ return true;
+}
+
+bool IRForTarget::ReplaceVariables(Function &llvm_function) {
+ if (!m_resolve_vars)
+ return true;
+
+ lldb_private::Log *log(GetLog(LLDBLog::Expressions));
+
+ m_decl_map->DoStructLayout();
+
+ LLDB_LOG(log, "Element arrangement:");
+
+ uint32_t num_elements;
+ uint32_t element_index;
+
+ size_t size;
+ lldb::offset_t alignment;
+
+ if (!m_decl_map->GetStructInfo(num_elements, size, alignment))
+ return false;
+
+ Function::arg_iterator iter(llvm_function.arg_begin());
+
+ if (iter == llvm_function.arg_end()) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes no "
+ "arguments (should take at least a struct pointer)");
+
+ return false;
+ }
+
+ Argument *argument = &*iter;
+
+ if (argument->getName() == "this") {
+ ++iter;
+
+ if (iter == llvm_function.arg_end()) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes only "
+ "'this' argument (should take a struct pointer "
+ "too)");
+
+ return false;
+ }
+
+ argument = &*iter;
+ } else if (argument->getName() == "self") {
+ ++iter;
+
+ if (iter == llvm_function.arg_end()) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes only "
+ "'self' argument (should take '_cmd' and a struct "
+ "pointer too)");
+
+ return false;
+ }
+
+ if (iter->getName() != "_cmd") {
+ m_error_stream.Format("Internal error [IRForTarget]: Wrapper takes '{0}' "
+ "after 'self' argument (should take '_cmd')",
+ iter->getName());
+
+ return false;
+ }
+
+ ++iter;
+
+ if (iter == llvm_function.arg_end()) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Wrapper takes only "
+ "'self' and '_cmd' arguments (should take a struct "
+ "pointer too)");
+
+ return false;
+ }
+
+ argument = &*iter;
+ }
+
+ if (argument->getName() != "$__lldb_arg") {
+ m_error_stream.Format("Internal error [IRForTarget]: Wrapper takes an "
+ "argument named '{0}' instead of the struct pointer",
+ argument->getName());
+
+ return false;
+ }
+
+ LLDB_LOG(log, "Arg: \"{0}\"", PrintValue(argument));
+
+ BasicBlock &entry_block(llvm_function.getEntryBlock());
+ Instruction *FirstEntryInstruction(entry_block.getFirstNonPHIOrDbg());
+
+ if (!FirstEntryInstruction) {
+ m_error_stream.Printf("Internal error [IRForTarget]: Couldn't find the "
+ "first instruction in the wrapper for use in "
+ "rewriting");
+
+ return false;
+ }
+
+ LLVMContext &context(m_module->getContext());
+ IntegerType *offset_type(Type::getInt32Ty(context));
+
+ if (!offset_type) {
+ m_error_stream.Printf(
+ "Internal error [IRForTarget]: Couldn't produce an offset type");
+
+ return false;
+ }
+
+ for (element_index = 0; element_index < num_elements; ++element_index) {
+ const clang::NamedDecl *decl = nullptr;
+ Value *value = nullptr;
+ lldb::offset_t offset;
+ lldb_private::ConstString name;
+
+ if (!m_decl_map->GetStructElement(decl, value, offset, name,
+ element_index)) {
+ m_error_stream.Printf(
+ "Internal error [IRForTarget]: Structure information is incomplete");
+
+ return false;
+ }
+
+ LLDB_LOG(log, " \"{0}\" (\"{1}\") placed at {2}", name,
+ decl->getNameAsString(), offset);
+
+ if (value) {
+ LLDB_LOG(log, " Replacing [{0}]", PrintValue(value));
+
+ FunctionValueCache body_result_maker(
+ [this, name, offset_type, offset, argument,
+ value](llvm::Function *function) -> llvm::Value * {
+ // Per the comment at ASTResultSynthesizer::SynthesizeBodyResult,
+ // in cases where the result variable is an rvalue, we have to
+ // synthesize a dereference of the appropriate structure entry in
+ // order to produce the static variable that the AST thinks it is
+ // accessing.
+
+ llvm::Instruction *entry_instruction = llvm::cast<Instruction>(
+ m_entry_instruction_finder.GetValue(function));
+
+ Type *int8Ty = Type::getInt8Ty(function->getContext());
+ ConstantInt *offset_int(
+ ConstantInt::get(offset_type, offset, true));
+ GetElementPtrInst *get_element_ptr = GetElementPtrInst::Create(
+ int8Ty, argument, offset_int, "", entry_instruction);
+
+ if (name == m_result_name && !m_result_is_pointer) {
+ LoadInst *load = new LoadInst(value->getType(), get_element_ptr,
+ "", entry_instruction);
+
+ return load;
+ } else {
+ return get_element_ptr;
+ }
+ });
+
+ if (Constant *constant = dyn_cast<Constant>(value)) {
+ if (!UnfoldConstant(constant, &llvm_function, body_result_maker,
+ m_entry_instruction_finder, m_error_stream)) {
+ return false;
+ }
+ } else if (Instruction *instruction = dyn_cast<Instruction>(value)) {
+ if (instruction->getParent()->getParent() != &llvm_function) {
+ m_error_stream.PutCString("error: Capturing non-local variables in "
+ "expressions is unsupported.\n");
+ return false;
+ }
+ value->replaceAllUsesWith(
+ body_result_maker.GetValue(instruction->getParent()->getParent()));
+ } else {
+ LLDB_LOG(log, "Unhandled non-constant type: \"{0}\"",
+ PrintValue(value));
+ return false;
+ }
+
+ if (GlobalVariable *var = dyn_cast<GlobalVariable>(value))
+ var->eraseFromParent();
+ }
+ }
+
+ LLDB_LOG(log, "Total structure [align {0}, size {1}]", (int64_t)alignment,
+ (uint64_t)size);
+
+ return true;
+}
+
+bool IRForTarget::runOnModule(Module &llvm_module) {
+ lldb_private::Log *log(GetLog(LLDBLog::Expressions));
+
+ m_module = &llvm_module;
+ m_target_data = std::make_unique<DataLayout>(m_module);
+ m_intptr_ty = llvm::Type::getIntNTy(m_module->getContext(),
+ m_target_data->getPointerSizeInBits());
+
+ if (log) {
+ std::string s;
+ raw_string_ostream oss(s);
+
+ m_module->print(oss, nullptr);
+
+ oss.flush();
+
+ LLDB_LOG(log, "Module as passed in to IRForTarget: \n\"{0}\"", s);
+ }
+
+ Function *const main_function =
+ m_func_name.IsEmpty() ? nullptr
+ : m_module->getFunction(m_func_name.GetStringRef());
+
+ if (!m_func_name.IsEmpty() && !main_function) {
+ LLDB_LOG(log, "Couldn't find \"{0}()\" in the module", m_func_name);
+
+ m_error_stream.Format("Internal error [IRForTarget]: Couldn't find wrapper "
+ "'{0}' in the module",
+ m_func_name);
+
+ return false;
+ }
+
+ if (main_function) {
+ if (!FixFunctionLinkage(*main_function)) {
+ LLDB_LOG(log, "Couldn't fix the linkage for the function");
+
+ return false;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////
+ // Replace $__lldb_expr_result with a persistent variable
+ //
+
+ if (main_function) {
+ if (!CreateResultVariable(*main_function)) {
+ LLDB_LOG(log, "CreateResultVariable() failed");
+
+ // CreateResultVariable() reports its own errors, so we don't do so here
+
+ return false;
+ }
+ }
+
+ if (log && log->GetVerbose()) {
+ std::string s;
+ raw_string_ostream oss(s);
+
+ m_module->print(oss, nullptr);
+
+ oss.flush();
+
+ LLDB_LOG(log, "Module after creating the result variable: \n\"{0}\"", s);
+ }
+
+ for (llvm::Function &function : *m_module) {
+ for (BasicBlock &bb : function) {
+ if (!RemoveGuards(bb)) {
+ LLDB_LOG(log, "RemoveGuards() failed");
+
+ // RemoveGuards() reports its own errors, so we don't do so here
+
+ return false;
+ }
+
+ if (!RewritePersistentAllocs(bb)) {
+ LLDB_LOG(log, "RewritePersistentAllocs() failed");
+
+ // RewritePersistentAllocs() reports its own errors, so we don't do so
+ // here
+
+ return false;
+ }
+
+ if (!RemoveCXAAtExit(bb)) {
+ LLDB_LOG(log, "RemoveCXAAtExit() failed");
+
+ // RemoveCXAAtExit() reports its own errors, so we don't do so here
+
+ return false;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // Fix all Objective-C constant strings to use NSStringWithCString:encoding:
+ //
+
+ if (!RewriteObjCConstStrings()) {
+ LLDB_LOG(log, "RewriteObjCConstStrings() failed");
+
+ // RewriteObjCConstStrings() reports its own errors, so we don't do so here
+
+ return false;
+ }
+
+ for (llvm::Function &function : *m_module) {
+ for (llvm::BasicBlock &bb : function) {
+ if (!RewriteObjCSelectors(bb)) {
+ LLDB_LOG(log, "RewriteObjCSelectors() failed");
+
+ // RewriteObjCSelectors() reports its own errors, so we don't do so
+ // here
+
+ return false;
+ }
+ }
+ }
+
+ for (llvm::Function &function : *m_module) {
+ for (BasicBlock &bb : function) {
+ if (!ResolveCalls(bb)) {
+ LLDB_LOG(log, "ResolveCalls() failed");
+
+ // ResolveCalls() reports its own errors, so we don't do so here
+
+ return false;
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Run function-level passes that only make sense on the main function
+ //
+
+ if (main_function) {
+ if (!ResolveExternals(*main_function)) {
+ LLDB_LOG(log, "ResolveExternals() failed");
+
+ // ResolveExternals() reports its own errors, so we don't do so here
+
+ return false;
+ }
+
+ if (!ReplaceVariables(*main_function)) {
+ LLDB_LOG(log, "ReplaceVariables() failed");
+
+ // ReplaceVariables() reports its own errors, so we don't do so here
+
+ return false;
+ }
+ }
+
+ if (log && log->GetVerbose()) {
+ std::string s;
+ raw_string_ostream oss(s);
+
+ m_module->print(oss, nullptr);
+
+ oss.flush();
+
+ LLDB_LOG(log, "Module after preparing for execution: \n\"{0}\"", s);
+ }
+
+ return true;
+}