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