//===-- RenderScriptExpressionOpts.cpp --------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include "llvm/ADT/None.h" #include "llvm/ADT/StringRef.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetOptions.h" #include "clang/Basic/TargetOptions.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Utility/Log.h" #include "RenderScriptExpressionOpts.h" #include "RenderScriptRuntime.h" #include "RenderScriptx86ABIFixups.h" using namespace lldb_private; using namespace lldb_renderscript; // [``slang``](https://android.googlesource.com/platform/frameworks/compile/slang), // the compiler frontend for RenderScript embeds an ARM specific triple in IR // that is shipped in the app, after generating IR that has some assumptions // that an ARM device is the target. As the IR is then compiled on a device of // unknown (at time the IR was generated at least) architecture, when calling // RenderScript API function as part of debugger expressions, we have to // perform a fixup pass that removes those assumptions right before the module // is sent to be generated by the llvm backend. namespace { bool registerRSDefaultTargetOpts(clang::TargetOptions &proto, const llvm::Triple::ArchType &arch) { switch (arch) { case llvm::Triple::ArchType::x86: proto.Triple = "i686--linux-android"; proto.CPU = "atom"; proto.Features.push_back("+long64"); // Fallthrough for common x86 family features LLVM_FALLTHROUGH; case llvm::Triple::ArchType::x86_64: proto.Features.push_back("+mmx"); proto.Features.push_back("+sse"); proto.Features.push_back("+sse2"); proto.Features.push_back("+sse3"); proto.Features.push_back("+ssse3"); proto.Features.push_back("+sse4.1"); proto.Features.push_back("+sse4.2"); break; case llvm::Triple::ArchType::mipsel: // pretend this is `arm' for the front-end proto.Triple = "armv7-none-linux-android"; proto.CPU = ""; proto.Features.push_back("+long64"); break; case llvm::Triple::ArchType::mips64el: // pretend this is `aarch64' for the front-end proto.Triple = "aarch64-none-linux-android"; proto.CPU = ""; break; default: return false; } return true; } } // end anonymous namespace bool RenderScriptRuntimeModulePass::runOnModule(llvm::Module &module) { bool changed_module = false; Log *log( GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_EXPRESSIONS)); std::string err; llvm::StringRef real_triple = m_process_ptr->GetTarget().GetArchitecture().GetTriple().getTriple(); const llvm::Target *target_info = llvm::TargetRegistry::lookupTarget(real_triple, err); if (!target_info) { if (log) log->Warning("couldn't determine real target architecture: '%s'", err.c_str()); return false; } llvm::Optional reloc_model = llvm::None; assert(m_process_ptr && "no available lldb process"); switch (m_process_ptr->GetTarget().GetArchitecture().GetMachine()) { case llvm::Triple::ArchType::x86: changed_module |= fixupX86FunctionCalls(module); // For some reason this triple gets totally missed by the backend, and must // be set manually. There a reference in bcc/Main.cpp about auto feature- // detection being removed from LLVM3.5, but I can't see that discussion // anywhere public. real_triple = "i686--linux-android"; break; case llvm::Triple::ArchType::x86_64: changed_module |= fixupX86_64FunctionCalls(module); break; case llvm::Triple::ArchType::mipsel: case llvm::Triple::ArchType::mips64el: // No actual IR fixup pass is needed on MIPS, but the datalayout and // targetmachine do need to be explicitly set. // bcc explicitly compiles MIPS code to use the static relocation model due // to an issue with relocations in mclinker. see // libbcc/support/CompilerConfig.cpp for details reloc_model = llvm::Reloc::Static; changed_module = true; break; case llvm::Triple::ArchType::arm: case llvm::Triple::ArchType::aarch64: // ARM subtargets need no fixup passes as they are the initial target as // generated by the // slang compiler frontend. break; default: if (log) log->Warning("Ignoring unknown renderscript target"); return false; } if (changed_module) { llvm::TargetOptions options; llvm::TargetMachine *target_machine = target_info->createTargetMachine( real_triple, "", "", options, reloc_model); assert(target_machine && "failed to identify RenderScriptRuntime target machine"); // We've been using a triple and datalayout of some ARM variant all along, // so we need to let the backend know that this is no longer the case. if (log) { log->Printf("%s - Changing RS target triple to '%s'", __FUNCTION__, real_triple.str().c_str()); log->Printf( "%s - Changing RS datalayout to '%s'", __FUNCTION__, target_machine->createDataLayout().getStringRepresentation().c_str()); } module.setTargetTriple(real_triple); module.setDataLayout(target_machine->createDataLayout()); } return changed_module; } char RenderScriptRuntimeModulePass::ID = 0; namespace lldb_private { bool RenderScriptRuntime::GetOverrideExprOptions(clang::TargetOptions &proto) { auto *process = GetProcess(); assert(process); return registerRSDefaultTargetOpts( proto, process->GetTarget().GetArchitecture().GetMachine()); } bool RenderScriptRuntime::GetIRPasses(LLVMUserExpression::IRPasses &passes) { if (!m_ir_passes) m_ir_passes = new RSIRPasses(GetProcess()); assert(m_ir_passes); passes.EarlyPasses = m_ir_passes->EarlyPasses; passes.LatePasses = m_ir_passes->LatePasses; return true; } namespace lldb_renderscript { RSIRPasses::RSIRPasses(Process *process) { IRPasses(); assert(process); EarlyPasses = std::make_shared(); assert(EarlyPasses); EarlyPasses->add(new RenderScriptRuntimeModulePass(process)); } RSIRPasses::~RSIRPasses() {} } // namespace lldb_renderscript } // namespace lldb_private