1//===-- RenderScriptx86ABIFixups.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 <set> 10 11#include "llvm/ADT/StringRef.h" 12#include "llvm/IR/BasicBlock.h" 13#include "llvm/IR/Constants.h" 14#include "llvm/IR/Function.h" 15#include "llvm/IR/Instruction.h" 16#include "llvm/IR/Instructions.h" 17#include "llvm/IR/Module.h" 18#include "llvm/IRReader/IRReader.h" 19#include "llvm/Pass.h" 20 21#include "lldb/Target/Process.h" 22#include "lldb/Utility/LLDBLog.h" 23#include "lldb/Utility/Log.h" 24 25using namespace lldb_private; 26 27static bool isRSAPICall(llvm::CallInst *call_inst) { 28 // TODO get the list of renderscript modules from lldb and check if 29 // this llvm::Module calls into any of them. 30 const auto func_name = call_inst->getCalledFunction()->getName(); 31 if (func_name.startswith("llvm") || func_name.startswith("lldb")) 32 return false; 33 34 if (call_inst->getCalledFunction()->isIntrinsic()) 35 return false; 36 37 return true; 38} 39 40static bool isRSLargeReturnCall(llvm::CallInst *call_inst) { 41 // i686 and x86_64 returns for large vectors in the RenderScript API are not 42 // handled as normal register pairs, but as a hidden sret type. This is not 43 // reflected in the debug info or mangled symbol name, and the android ABI 44 // for x86 and x86_64, (as well as the emulators) specifies there is no AVX, 45 // so bcc generates an sret function because we cannot natively return 46 // 256 bit vectors. 47 // This function simply checks whether a function has a > 128bit return type. 48 // It is perhaps an unreliable heuristic, and relies on bcc not generating 49 // AVX code, so if the android ABI one day provides for AVX, this function 50 // may go out of fashion. 51 if (!call_inst || !call_inst->getCalledFunction()) 52 return false; 53 54 return call_inst->getCalledFunction() 55 ->getReturnType() 56 ->getPrimitiveSizeInBits() > 128; 57} 58 59static bool isRSAllocationTy(const llvm::Type *type) { 60 return type->isStructTy() && 61 type->getStructName().startswith("struct.rs_allocation"); 62} 63 64static bool isRSAllocationTyCallSite(llvm::CallInst *call_inst) { 65 if (!call_inst->hasByValArgument()) 66 return false; 67 for (unsigned i = 0; i < call_inst->arg_size(); ++i) { 68 if (llvm::Type *ByValTy = call_inst->getParamByValType(i)) 69 if (isRSAllocationTy(ByValTy)) 70 return true; 71 } 72 return false; 73} 74 75static llvm::FunctionType *cloneToStructRetFnTy(llvm::CallInst *call_inst) { 76 // on x86 StructReturn functions return a pointer to the return value, rather 77 // than the return value itself 78 // [ref](http://www.agner.org/optimize/calling_conventions.pdf section 6). We 79 // create a return type by getting the pointer type of the old return type, 80 // and inserting a new initial argument of pointer type of the original 81 // return type. 82 Log *log = GetLog(LLDBLog::Language | LLDBLog::Expressions); 83 84 assert(call_inst && "no CallInst"); 85 llvm::Function *orig = call_inst->getCalledFunction(); 86 assert(orig && "CallInst has no called function"); 87 llvm::FunctionType *orig_type = orig->getFunctionType(); 88 auto name = orig->getName(); 89 LLDB_LOGF(log, "%s - cloning to StructRet function for '%s'", __FUNCTION__, 90 name.str().c_str()); 91 92 unsigned num_params = orig_type->getNumParams(); 93 std::vector<llvm::Type *> new_params{num_params + 1, nullptr}; 94 std::vector<llvm::Type *> params{orig_type->param_begin(), 95 orig_type->param_end()}; 96 97 // This may not work if the function is somehow declared void as llvm is 98 // strongly typed and represents void* with i8* 99 assert(!orig_type->getReturnType()->isVoidTy() && 100 "Cannot add StructRet attribute to void function"); 101 llvm::PointerType *return_type_ptr_type = 102 llvm::PointerType::getUnqual(orig->getReturnType()); 103 assert(return_type_ptr_type && 104 "failed to get function return type PointerType"); 105 if (!return_type_ptr_type) 106 return nullptr; 107 108 LLDB_LOGF(log, 109 "%s - return type pointer type for StructRet clone @ '0x%p':\n", 110 __FUNCTION__, (void *)return_type_ptr_type); 111 // put the sret pointer argument in place at the beginning of the 112 // argument list. 113 params.emplace(params.begin(), return_type_ptr_type); 114 assert(params.size() == num_params + 1); 115 return llvm::FunctionType::get(return_type_ptr_type, params, 116 orig->isVarArg()); 117} 118 119static bool 120findRSCallSites(llvm::Module &module, std::set<llvm::CallInst *> &rs_callsites, 121 bool (*predicate)(llvm::CallInst *)) { 122 bool found = false; 123 124 for (auto &func : module.getFunctionList()) 125 for (auto &block : func) 126 for (auto &inst : block) { 127 llvm::CallInst *call_inst = 128 llvm::dyn_cast_or_null<llvm::CallInst>(&inst); 129 if (!call_inst || !call_inst->getCalledFunction()) 130 // This is not the call-site you are looking for... 131 continue; 132 if (isRSAPICall(call_inst) && predicate(call_inst)) { 133 rs_callsites.insert(call_inst); 134 found = true; 135 } 136 } 137 return found; 138} 139 140static bool fixupX86StructRetCalls(llvm::Module &module) { 141 bool changed = false; 142 // changing a basic block while iterating over it seems to have some 143 // undefined behaviour going on so we find all RS callsites first, then fix 144 // them up after consuming the iterator. 145 std::set<llvm::CallInst *> rs_callsites; 146 if (!findRSCallSites(module, rs_callsites, isRSLargeReturnCall)) 147 return false; 148 149 for (auto call_inst : rs_callsites) { 150 llvm::FunctionType *new_func_type = cloneToStructRetFnTy(call_inst); 151 assert(new_func_type && 152 "failed to clone functionType for Renderscript ABI fixup"); 153 154 llvm::Function *func = call_inst->getCalledFunction(); 155 assert(func && "cannot resolve function in RenderScriptRuntime"); 156 // Copy the original call arguments 157 std::vector<llvm::Value *> new_call_args(call_inst->arg_begin(), 158 call_inst->arg_end()); 159 160 // Allocate enough space to store the return value of the original function 161 // we pass a pointer to this allocation as the StructRet param, and then 162 // copy its value into the lldb return value 163 const llvm::DataLayout &DL = module.getDataLayout(); 164 llvm::AllocaInst *return_value_alloc = new llvm::AllocaInst( 165 func->getReturnType(), DL.getAllocaAddrSpace(), "var_vector_return_alloc", 166 call_inst); 167 // use the new allocation as the new first argument 168 new_call_args.emplace(new_call_args.begin(), 169 llvm::cast<llvm::Value>(return_value_alloc)); 170 llvm::PointerType *new_func_ptr_type = 171 llvm::PointerType::get(new_func_type, 0); 172 // Create the type cast from the old function type to the new one 173 llvm::Constant *new_func_cast = llvm::ConstantExpr::getCast( 174 llvm::Instruction::BitCast, func, new_func_ptr_type); 175 // create an allocation for a new function pointer 176 llvm::AllocaInst *new_func_ptr = 177 new llvm::AllocaInst(new_func_ptr_type, DL.getAllocaAddrSpace(), 178 "new_func_ptr", call_inst); 179 // store the new_func_cast to the newly allocated space 180 (new llvm::StoreInst(new_func_cast, new_func_ptr, call_inst)) 181 ->setName("new_func_ptr_load_cast"); 182 // load the new function address ready for a jump 183 llvm::LoadInst *new_func_addr_load = new llvm::LoadInst( 184 new_func_ptr_type, new_func_ptr, "load_func_pointer", call_inst); 185 // and create a callinstruction from it 186 llvm::CallInst *new_call_inst = 187 llvm::CallInst::Create(new_func_type, new_func_addr_load, new_call_args, 188 "new_func_call", call_inst); 189 new_call_inst->setCallingConv(call_inst->getCallingConv()); 190 new_call_inst->setTailCall(call_inst->isTailCall()); 191 llvm::LoadInst *lldb_save_result_address = 192 new llvm::LoadInst(func->getReturnType(), return_value_alloc, 193 "save_return_val", call_inst); 194 195 // Now remove the old broken call 196 call_inst->replaceAllUsesWith(lldb_save_result_address); 197 call_inst->eraseFromParent(); 198 changed = true; 199 } 200 return changed; 201} 202 203static bool fixupRSAllocationStructByValCalls(llvm::Module &module) { 204 // On x86_64, calls to functions in the RS runtime that take an 205 // `rs_allocation` type argument are actually handled as by-ref params by 206 // bcc, but appear to be passed by value by lldb (the callsite all use 207 // `struct byval`). On x86_64 Linux, struct arguments are transferred in 208 // registers if the struct size is no bigger than 128bits 209 // [ref](http://www.agner.org/optimize/calling_conventions.pdf) section 7.1 210 // "Passing and returning objects" otherwise passed on the stack. an object 211 // of type `rs_allocation` is actually 256bits, so should be passed on the 212 // stack. However, code generated by bcc actually treats formal params of 213 // type `rs_allocation` as `rs_allocation *` so we need to convert the 214 // calling convention to pass by reference, and remove any hint of byval from 215 // formal parameters. 216 bool changed = false; 217 std::set<llvm::CallInst *> rs_callsites; 218 if (!findRSCallSites(module, rs_callsites, isRSAllocationTyCallSite)) 219 return false; 220 221 std::set<llvm::Function *> rs_functions; 222 223 // for all call instructions 224 for (auto call_inst : rs_callsites) { 225 // add the called function to a set so that we can strip its byval 226 // attributes in another pass 227 rs_functions.insert(call_inst->getCalledFunction()); 228 229 // get the function attributes 230 llvm::AttributeList call_attribs = call_inst->getAttributes(); 231 232 // iterate over the argument attributes 233 for (unsigned I : call_attribs.indexes()) { 234 // if this argument is passed by val 235 if (call_attribs.hasAttributeAtIndex(I, llvm::Attribute::ByVal)) { 236 // strip away the byval attribute 237 call_inst->removeAttributeAtIndex(I, llvm::Attribute::ByVal); 238 changed = true; 239 } 240 } 241 } 242 243 // for all called function decls 244 for (auto func : rs_functions) { 245 // inspect all of the arguments in the call 246 for (auto &arg : func->args()) { 247 if (arg.hasByValAttr()) { 248 arg.removeAttr(llvm::Attribute::ByVal); 249 changed = true; 250 } 251 } 252 } 253 return changed; 254} 255 256namespace lldb_private { 257namespace lldb_renderscript { 258 259bool fixupX86FunctionCalls(llvm::Module &module) { 260 return fixupX86StructRetCalls(module); 261} 262 263bool fixupX86_64FunctionCalls(llvm::Module &module) { 264 bool changed = false; 265 changed |= fixupX86StructRetCalls(module); 266 changed |= fixupRSAllocationStructByValCalls(module); 267 return changed; 268} 269 270} // end namespace lldb_renderscript 271} // end namespace lldb_private 272