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