1 //===-- RenderScriptExpressionOpts.cpp ------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include <string>
10 
11 #include "llvm/ADT/StringRef.h"
12 #include "llvm/IR/Instruction.h"
13 #include "llvm/IR/Instructions.h"
14 #include "llvm/IR/LegacyPassManager.h"
15 #include "llvm/IR/Module.h"
16 #include "llvm/MC/TargetRegistry.h"
17 #include "llvm/Target/TargetMachine.h"
18 #include "llvm/Target/TargetOptions.h"
19 
20 #include "clang/Basic/TargetOptions.h"
21 
22 #include "lldb/Target/Process.h"
23 #include "lldb/Target/Target.h"
24 #include "lldb/Utility/LLDBLog.h"
25 #include "lldb/Utility/Log.h"
26 
27 #include "RenderScriptExpressionOpts.h"
28 #include "RenderScriptRuntime.h"
29 #include "RenderScriptx86ABIFixups.h"
30 
31 using namespace lldb_private;
32 using namespace lldb_renderscript;
33 
34 // [``slang``](https://android.googlesource.com/platform/frameworks/compile/slang),
35 // the compiler frontend for RenderScript embeds an ARM specific triple in IR
36 // that is shipped in the app, after generating IR that has some assumptions
37 // that an ARM device is the target. As the IR is then compiled on a device of
38 // unknown (at time the IR was generated at least) architecture, when calling
39 // RenderScript API function as part of debugger expressions, we have to
40 // perform a fixup pass that removes those assumptions right before the module
41 // is sent to be generated by the llvm backend.
42 
registerRSDefaultTargetOpts(clang::TargetOptions & proto,const llvm::Triple::ArchType & arch)43 static bool registerRSDefaultTargetOpts(clang::TargetOptions &proto,
44                                         const llvm::Triple::ArchType &arch) {
45   switch (arch) {
46   case llvm::Triple::ArchType::x86:
47     proto.Triple = "i686--linux-android";
48     proto.CPU = "atom";
49     proto.Features.push_back("+long64");
50     // Fallthrough for common x86 family features
51     [[fallthrough]];
52   case llvm::Triple::ArchType::x86_64:
53     proto.Features.push_back("+mmx");
54     proto.Features.push_back("+sse");
55     proto.Features.push_back("+sse2");
56     proto.Features.push_back("+sse3");
57     proto.Features.push_back("+ssse3");
58     proto.Features.push_back("+sse4.1");
59     proto.Features.push_back("+sse4.2");
60     break;
61   case llvm::Triple::ArchType::mipsel:
62     // pretend this is `arm' for the front-end
63     proto.Triple = "armv7-none-linux-android";
64     proto.CPU = "";
65     proto.Features.push_back("+long64");
66     break;
67   case llvm::Triple::ArchType::mips64el:
68     // pretend this is `aarch64' for the front-end
69     proto.Triple = "aarch64-none-linux-android";
70     proto.CPU = "";
71     break;
72   default:
73     return false;
74   }
75   return true;
76 }
77 
runOnModule(llvm::Module & module)78 bool RenderScriptRuntimeModulePass::runOnModule(llvm::Module &module) {
79   bool changed_module = false;
80   Log *log = GetLog(LLDBLog::Language | LLDBLog::Expressions);
81 
82   std::string err;
83   llvm::StringRef real_triple =
84       m_process_ptr->GetTarget().GetArchitecture().GetTriple().getTriple();
85   const llvm::Target *target_info =
86       llvm::TargetRegistry::lookupTarget(std::string(real_triple), err);
87   if (!target_info) {
88     if (log)
89       log->Warning("couldn't determine real target architecture: '%s'",
90                    err.c_str());
91     return false;
92   }
93 
94   std::optional<llvm::Reloc::Model> reloc_model;
95   assert(m_process_ptr && "no available lldb process");
96   switch (m_process_ptr->GetTarget().GetArchitecture().GetMachine()) {
97   case llvm::Triple::ArchType::x86:
98     changed_module |= fixupX86FunctionCalls(module);
99     // For some reason this triple gets totally missed by the backend, and must
100     // be set manually. There a reference in bcc/Main.cpp about auto feature-
101     // detection being removed from LLVM3.5, but I can't see that discussion
102     // anywhere public.
103     real_triple = "i686--linux-android";
104     break;
105   case llvm::Triple::ArchType::x86_64:
106     changed_module |= fixupX86_64FunctionCalls(module);
107     break;
108   case llvm::Triple::ArchType::mipsel:
109   case llvm::Triple::ArchType::mips64el:
110     // No actual IR fixup pass is needed on MIPS, but the datalayout and
111     // targetmachine do need to be explicitly set.
112 
113     // bcc explicitly compiles MIPS code to use the static relocation model due
114     // to an issue with relocations in mclinker. see
115     // libbcc/support/CompilerConfig.cpp for details
116     reloc_model = llvm::Reloc::Static;
117     changed_module = true;
118     break;
119   case llvm::Triple::ArchType::arm:
120   case llvm::Triple::ArchType::aarch64:
121     // ARM subtargets need no fixup passes as they are the initial target as
122     // generated by the
123     // slang compiler frontend.
124     break;
125   default:
126     if (log)
127       log->Warning("Ignoring unknown renderscript target");
128     return false;
129   }
130 
131   if (changed_module) {
132     llvm::TargetOptions options;
133     llvm::TargetMachine *target_machine = target_info->createTargetMachine(
134         real_triple, "", "", options, reloc_model);
135     assert(target_machine &&
136            "failed to identify RenderScriptRuntime target machine");
137     // We've been using a triple and datalayout of some ARM variant all along,
138     // so we need to let the backend know that this is no longer the case.
139     if (log) {
140       LLDB_LOGF(log, "%s - Changing RS target triple to '%s'", __FUNCTION__,
141                 real_triple.str().c_str());
142       LLDB_LOGF(
143           log, "%s - Changing RS datalayout to '%s'", __FUNCTION__,
144           target_machine->createDataLayout().getStringRepresentation().c_str());
145     }
146     module.setTargetTriple(real_triple);
147     module.setDataLayout(target_machine->createDataLayout());
148   }
149   return changed_module;
150 }
151 
152 char RenderScriptRuntimeModulePass::ID = 0;
153 
154 namespace lldb_private {
155 
GetOverrideExprOptions(clang::TargetOptions & proto)156 bool RenderScriptRuntime::GetOverrideExprOptions(clang::TargetOptions &proto) {
157   auto *process = GetProcess();
158   assert(process);
159   return registerRSDefaultTargetOpts(
160       proto, process->GetTarget().GetArchitecture().GetMachine());
161 }
162 
GetIRPasses(LLVMUserExpression::IRPasses & passes)163 bool RenderScriptRuntime::GetIRPasses(LLVMUserExpression::IRPasses &passes) {
164   if (!m_ir_passes)
165     m_ir_passes = new RSIRPasses(GetProcess());
166   assert(m_ir_passes);
167 
168   passes.EarlyPasses = m_ir_passes->EarlyPasses;
169   passes.LatePasses = m_ir_passes->LatePasses;
170 
171   return true;
172 }
173 
174 namespace lldb_renderscript {
175 
RSIRPasses(Process * process)176 RSIRPasses::RSIRPasses(Process *process) {
177   assert(process);
178 
179   EarlyPasses = std::make_shared<llvm::legacy::PassManager>();
180   assert(EarlyPasses);
181   EarlyPasses->add(new RenderScriptRuntimeModulePass(process));
182 }
183 
184 RSIRPasses::~RSIRPasses() = default;
185 
186 } // namespace lldb_renderscript
187 } // namespace lldb_private
188