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