Mips16HardFloat.cpp revision 5e4b95b3fe908f89aa512b6e9921fe49aadd759b
1//===---- Mips16HardFloat.cpp for Mips16 Hard Float --------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file defines a pass needed for Mips16 Hard Float 11// 12//===----------------------------------------------------------------------===// 13 14#define DEBUG_TYPE "mips16-hard-float" 15#include "Mips16HardFloat.h" 16#include "llvm/IR/Module.h" 17#include "llvm/Support/Debug.h" 18#include "llvm/Support/raw_ostream.h" 19#include <algorithm> 20#include <string> 21 22static void inlineAsmOut 23 (LLVMContext &C, StringRef AsmString, BasicBlock *BB ) { 24 std::vector<llvm::Type *> AsmArgTypes; 25 std::vector<llvm::Value*> AsmArgs; 26 llvm::FunctionType *AsmFTy = 27 llvm::FunctionType::get(Type::getVoidTy(C), 28 AsmArgTypes, false); 29 llvm::InlineAsm *IA = 30 llvm::InlineAsm::get(AsmFTy, AsmString, "", true, 31 /* IsAlignStack */ false, 32 llvm::InlineAsm::AD_ATT); 33 CallInst::Create(IA, AsmArgs, "", BB); 34} 35 36namespace { 37 38class InlineAsmHelper { 39 LLVMContext &C; 40 BasicBlock *BB; 41public: 42 InlineAsmHelper(LLVMContext &C_, BasicBlock *BB_) : 43 C(C_), BB(BB_) { 44 } 45 46 void Out(StringRef AsmString) { 47 inlineAsmOut(C, AsmString, BB); 48 } 49 50}; 51} 52// 53// Return types that matter for hard float are: 54// float, double, complex float, and complex double 55// 56enum FPReturnVariant { 57 FRet, DRet, CFRet, CDRet, NoFPRet 58}; 59 60// 61// Determine which FP return type this function has 62// 63static FPReturnVariant whichFPReturnVariant(Type *T) { 64 switch (T->getTypeID()) { 65 case Type::FloatTyID: 66 return FRet; 67 case Type::DoubleTyID: 68 return DRet; 69 case Type::StructTyID: 70 if (T->getStructNumElements() != 2) 71 break; 72 if ((T->getContainedType(0)->isFloatTy()) && 73 (T->getContainedType(1)->isFloatTy())) 74 return CFRet; 75 if ((T->getContainedType(0)->isDoubleTy()) && 76 (T->getContainedType(1)->isDoubleTy())) 77 return CDRet; 78 break; 79 default: 80 break; 81 } 82 return NoFPRet; 83} 84 85// 86// Parameter type that matter are float, (float, float), (float, double), 87// double, (double, double), (double, float) 88// 89enum FPParamVariant { 90 FSig, FFSig, FDSig, 91 DSig, DDSig, DFSig, NoSig 92}; 93 94// which floating point parameter signature variant we are dealing with 95// 96typedef Type::TypeID TypeID; 97const Type::TypeID FloatTyID = Type::FloatTyID; 98const Type::TypeID DoubleTyID = Type::DoubleTyID; 99 100static FPParamVariant whichFPParamVariantNeeded(Function &F) { 101 switch (F.arg_size()) { 102 case 0: 103 return NoSig; 104 case 1:{ 105 TypeID ArgTypeID = F.getFunctionType()->getParamType(0)->getTypeID(); 106 switch (ArgTypeID) { 107 case FloatTyID: 108 return FSig; 109 case DoubleTyID: 110 return DSig; 111 default: 112 return NoSig; 113 } 114 } 115 default: { 116 TypeID ArgTypeID0 = F.getFunctionType()->getParamType(0)->getTypeID(); 117 TypeID ArgTypeID1 = F.getFunctionType()->getParamType(1)->getTypeID(); 118 switch(ArgTypeID0) { 119 case FloatTyID: { 120 switch (ArgTypeID1) { 121 case FloatTyID: 122 return FFSig; 123 case DoubleTyID: 124 return FDSig; 125 default: 126 return FSig; 127 } 128 } 129 case DoubleTyID: { 130 switch (ArgTypeID1) { 131 case FloatTyID: 132 return DFSig; 133 case DoubleTyID: 134 return DDSig; 135 default: 136 return DSig; 137 } 138 } 139 default: 140 return NoSig; 141 } 142 } 143 } 144 llvm_unreachable("can't get here"); 145} 146 147// Figure out if we need float point based on the function parameters. 148// We need to move variables in and/or out of floating point 149// registers because of the ABI 150// 151static bool needsFPStubFromParams(Function &F) { 152 if (F.arg_size() >=1) { 153 Type *ArgType = F.getFunctionType()->getParamType(0); 154 switch (ArgType->getTypeID()) { 155 case Type::FloatTyID: 156 case Type::DoubleTyID: 157 return true; 158 default: 159 break; 160 } 161 } 162 return false; 163} 164 165static bool needsFPReturnHelper(Function &F) { 166 Type* RetType = F.getReturnType(); 167 return whichFPReturnVariant(RetType) != NoFPRet; 168} 169 170static bool needsFPHelperFromSig(Function &F) { 171 return needsFPStubFromParams(F) || needsFPReturnHelper(F); 172} 173 174// 175// We swap between FP and Integer registers to allow Mips16 and Mips32 to 176// interoperate 177// 178 179static void swapFPIntParams 180 (FPParamVariant PV, Module *M, InlineAsmHelper &IAH, 181 bool LE, bool ToFP) { 182 //LLVMContext &Context = M->getContext(); 183 std::string MI = ToFP? "mtc1 ": "mfc1 "; 184 switch (PV) { 185 case FSig: 186 IAH.Out(MI + "$$4,$$f12"); 187 break; 188 case FFSig: 189 IAH.Out(MI +"$$4,$$f12"); 190 IAH.Out(MI + "$$5,$$f14"); 191 break; 192 case FDSig: 193 IAH.Out(MI + "$$4,$$f12"); 194 if (LE) { 195 IAH.Out(MI + "$$6,$$f14"); 196 IAH.Out(MI + "$$7,$$f15"); 197 } else { 198 IAH.Out(MI + "$$7,$$f14"); 199 IAH.Out(MI + "$$6,$$f15"); 200 } 201 break; 202 case DSig: 203 if (LE) { 204 IAH.Out(MI + "$$4,$$f12"); 205 IAH.Out(MI + "$$5,$$f13"); 206 } else { 207 IAH.Out(MI + "$$5,$$f12"); 208 IAH.Out(MI + "$$4,$$f13"); 209 } 210 break; 211 case DDSig: 212 if (LE) { 213 IAH.Out(MI + "$$4,$$f12"); 214 IAH.Out(MI + "$$5,$$f13"); 215 IAH.Out(MI + "$$6,$$f14"); 216 IAH.Out(MI + "$$7,$$f15"); 217 } else { 218 IAH.Out(MI + "$$5,$$f12"); 219 IAH.Out(MI + "$$4,$$f13"); 220 IAH.Out(MI + "$$7,$$f14"); 221 IAH.Out(MI + "$$6,$$f15"); 222 } 223 break; 224 case DFSig: 225 if (LE) { 226 IAH.Out(MI + "$$4,$$f12"); 227 IAH.Out(MI + "$$5,$$f13"); 228 } else { 229 IAH.Out(MI + "$$5,$$f12"); 230 IAH.Out(MI + "$$4,$$f13"); 231 } 232 IAH.Out(MI + "$$6,$$f14"); 233 break; 234 case NoSig: 235 return; 236 } 237} 238// 239// Make sure that we know we already need a stub for this function. 240// Having called needsFPHelperFromSig 241// 242static void assureFPCallStub(Function &F, Module *M, 243 const MipsSubtarget &Subtarget){ 244 // for now we only need them for static relocation 245 if (Subtarget.getRelocationModel() == Reloc::PIC_) 246 return; 247 LLVMContext &Context = M->getContext(); 248 bool LE = Subtarget.isLittle(); 249 std::string Name = F.getName(); 250 std::string SectionName = ".mips16.call.fp." + Name; 251 std::string StubName = "__call_stub_fp_" + Name; 252 // 253 // see if we already have the stub 254 // 255 Function *FStub = M->getFunction(StubName); 256 if (FStub && !FStub->isDeclaration()) return; 257 FStub = Function::Create(F.getFunctionType(), 258 Function::InternalLinkage, StubName, M); 259 FStub->addFnAttr("mips16_fp_stub"); 260 FStub->addFnAttr(llvm::Attribute::Naked); 261 FStub->addFnAttr(llvm::Attribute::NoInline); 262 FStub->addFnAttr(llvm::Attribute::NoUnwind); 263 FStub->addFnAttr("nomips16"); 264 FStub->setSection(SectionName); 265 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub); 266 InlineAsmHelper IAH(Context, BB); 267 IAH.Out(".set reorder"); 268 FPReturnVariant RV = whichFPReturnVariant(FStub->getReturnType()); 269 FPParamVariant PV = whichFPParamVariantNeeded(F); 270 swapFPIntParams(PV, M, IAH, LE, true); 271 if (RV != NoFPRet) { 272 IAH.Out("move $$18, $$31"); 273 IAH.Out("jal " + Name); 274 } else { 275 IAH.Out("lui $$25,%hi(" + Name + ")"); 276 IAH.Out("addiu $$25,$$25,%lo(" + Name + ")" ); 277 } 278 switch (RV) { 279 case FRet: 280 IAH.Out("mfc1 $$2,$$f0"); 281 break; 282 case DRet: 283 if (LE) { 284 IAH.Out("mfc1 $$2,$$f0"); 285 IAH.Out("mfc1 $$3,$$f1"); 286 } else { 287 IAH.Out("mfc1 $$3,$$f0"); 288 IAH.Out("mfc1 $$2,$$f1"); 289 } 290 break; 291 case CFRet: 292 if (LE) { 293 IAH.Out("mfc1 $$2,$$f0"); 294 IAH.Out("mfc1 $$3,$$f2"); 295 } else { 296 IAH.Out("mfc1 $$3,$$f0"); 297 IAH.Out("mfc1 $$3,$$f2"); 298 } 299 break; 300 case CDRet: 301 if (LE) { 302 IAH.Out("mfc1 $$4,$$f2"); 303 IAH.Out("mfc1 $$5,$$f3"); 304 IAH.Out("mfc1 $$2,$$f0"); 305 IAH.Out("mfc1 $$3,$$f1"); 306 307 } else { 308 IAH.Out("mfc1 $$5,$$f2"); 309 IAH.Out("mfc1 $$4,$$f3"); 310 IAH.Out("mfc1 $$3,$$f0"); 311 IAH.Out("mfc1 $$2,$$f1"); 312 } 313 break; 314 case NoFPRet: 315 break; 316 } 317 if (RV != NoFPRet) 318 IAH.Out("jr $$18"); 319 else 320 IAH.Out("jr $$25"); 321 new UnreachableInst(Context, BB); 322} 323 324// 325// Functions that are inline intrinsics don't need helpers. 326// 327std::string IntrinsicInline[] = 328 {"fabs"}; 329 330bool isIntrinsicInline(Function *F) { 331 return std::binary_search( 332 IntrinsicInline, array_endof(IntrinsicInline), 333 F->getName()); 334} 335// 336// Returns of float, double and complex need to be handled with a helper 337// function. 338// 339static bool fixupFPReturnAndCall 340 (Function &F, Module *M, const MipsSubtarget &Subtarget) { 341 bool Modified = false; 342 LLVMContext &C = M->getContext(); 343 Type *MyVoid = Type::getVoidTy(C); 344 for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB) 345 for (BasicBlock::iterator I = BB->begin(), E = BB->end(); 346 I != E; ++I) { 347 Instruction &Inst = *I; 348 if (const ReturnInst *RI = dyn_cast<ReturnInst>(I)) { 349 Value *RVal = RI->getReturnValue(); 350 if (!RVal) continue; 351 // 352 // If there is a return value and it needs a helper function, 353 // figure out which one and add a call before the actual 354 // return to this helper. The purpose of the helper is to move 355 // floating point values from their soft float return mapping to 356 // where they would have been mapped to in floating point registers. 357 // 358 Type *T = RVal->getType(); 359 FPReturnVariant RV = whichFPReturnVariant(T); 360 if (RV == NoFPRet) continue; 361 static const char* Helper[NoFPRet] = 362 {"__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc", 363 "__mips16_ret_dc"}; 364 const char *Name = Helper[RV]; 365 AttributeSet A; 366 Value *Params[] = {RVal}; 367 Modified = true; 368 // 369 // These helper functions have a different calling ABI so 370 // this __Mips16RetHelper indicates that so that later 371 // during call setup, the proper call lowering to the helper 372 // functions will take place. 373 // 374 A = A.addAttribute(C, AttributeSet::FunctionIndex, 375 "__Mips16RetHelper"); 376 A = A.addAttribute(C, AttributeSet::FunctionIndex, 377 Attribute::ReadNone); 378 A = A.addAttribute(C, AttributeSet::FunctionIndex, 379 Attribute::NoInline); 380 Value *F = (M->getOrInsertFunction(Name, A, MyVoid, T, NULL)); 381 CallInst::Create(F, Params, "", &Inst ); 382 } else if (const CallInst *CI = dyn_cast<CallInst>(I)) { 383 // pic mode calls are handled by already defined 384 // helper functions 385 if (Subtarget.getRelocationModel() != Reloc::PIC_ ) { 386 Function *F_ = CI->getCalledFunction(); 387 if (F_ && !isIntrinsicInline(F_) && needsFPHelperFromSig(*F_)) { 388 assureFPCallStub(*F_, M, Subtarget); 389 Modified=true; 390 } 391 } 392 } 393 } 394 return Modified; 395} 396 397static void createFPFnStub(Function *F, Module *M, FPParamVariant PV, 398 const MipsSubtarget &Subtarget ) { 399 bool PicMode = Subtarget.getRelocationModel() == Reloc::PIC_; 400 bool LE = Subtarget.isLittle(); 401 LLVMContext &Context = M->getContext(); 402 std::string Name = F->getName(); 403 std::string SectionName = ".mips16.fn." + Name; 404 std::string StubName = "__fn_stub_" + Name; 405 std::string LocalName = "__fn_local_" + Name; 406 Function *FStub = Function::Create 407 (F->getFunctionType(), 408 Function::InternalLinkage, StubName, M); 409 FStub->addFnAttr("mips16_fp_stub"); 410 FStub->addFnAttr(llvm::Attribute::Naked); 411 FStub->addFnAttr(llvm::Attribute::NoUnwind); 412 FStub->addFnAttr(llvm::Attribute::NoInline); 413 FStub->addFnAttr("nomips16"); 414 FStub->setSection(SectionName); 415 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub); 416 InlineAsmHelper IAH(Context, BB); 417 IAH.Out(" .set macro"); 418 if (PicMode) { 419 IAH.Out(".set noreorder"); 420 IAH.Out(".cpload $$2"); 421 IAH.Out(".set reorder"); 422 IAH.Out(".reloc 0,R_MIPS_NONE," + Name); 423 IAH.Out("la $$25," + LocalName); 424 } 425 else 426 IAH.Out("la $$25, " + Name); 427 swapFPIntParams(PV, M, IAH, LE, false); 428 IAH.Out("jr $$25"); 429 IAH.Out(LocalName + " = " + Name); 430 new UnreachableInst(FStub->getContext(), BB); 431} 432 433namespace llvm { 434 435// 436// This pass only makes sense when the underlying chip has floating point but 437// we are compiling as mips16. 438// For all mips16 functions (that are not stubs we have already generated), or 439// declared via attributes as nomips16, we must: 440// 1) fixup all returns of float, double, single and double complex 441// by calling a helper function before the actual return. 442// 2) generate helper functions (stubs) that can be called by mips32 functions 443// that will move parameters passed normally passed in floating point 444// registers the soft float equivalents. 445// 3) in the case of static relocation, generate helper functions so that 446// mips16 functions can call extern functions of unknown type (mips16 or 447// mips32). 448// 4) TBD. For pic, calls to extern functions of unknown type are handled by 449// predefined helper functions in libc but this work is currently done 450// during call lowering but it should be moved here in the future. 451// 452bool Mips16HardFloat::runOnModule(Module &M) { 453 DEBUG(errs() << "Run on Module Mips16HardFloat\n"); 454 bool Modified = false; 455 for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) { 456 if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") || 457 F->hasFnAttribute("nomips16")) continue; 458 Modified |= fixupFPReturnAndCall(*F, &M, Subtarget); 459 FPParamVariant V = whichFPParamVariantNeeded(*F); 460 if (V != NoSig) { 461 Modified = true; 462 createFPFnStub(F, &M, V, Subtarget); 463 } 464 } 465 return Modified; 466} 467 468char Mips16HardFloat::ID = 0; 469 470} 471 472ModulePass *llvm::createMips16HardFloat(MipsTargetMachine &TM) { 473 return new Mips16HardFloat(TM); 474} 475 476