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