1 //===---- Mips16HardFloat.cpp for Mips16 Hard Float --------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines a pass needed for Mips16 Hard Float
12 //===----------------------------------------------------------------------===//
14 #include "MipsTargetMachine.h"
15 #include "llvm/IR/Module.h"
16 #include "llvm/IR/Value.h"
17 #include "llvm/Support/Debug.h"
18 #include "llvm/Support/raw_ostream.h"
24 #define DEBUG_TYPE "mips16-hard-float"
27 class Mips16HardFloat : public ModulePass {
31 Mips16HardFloat(MipsTargetMachine &TM_) : ModulePass(ID), TM(TM_) {}
33 StringRef getPassName() const override { return "MIPS16 Hard Float Pass"; }
35 bool runOnModule(Module &M) override;
38 const MipsTargetMachine &TM;
41 static void EmitInlineAsm(LLVMContext &C, BasicBlock *BB, StringRef AsmText) {
42 std::vector<llvm::Type *> AsmArgTypes;
43 std::vector<llvm::Value *> AsmArgs;
45 llvm::FunctionType *AsmFTy =
46 llvm::FunctionType::get(Type::getVoidTy(C), AsmArgTypes, false);
48 llvm::InlineAsm::get(AsmFTy, AsmText, "", true,
49 /* IsAlignStack */ false, llvm::InlineAsm::AD_ATT);
50 CallInst::Create(IA, AsmArgs, "", BB);
53 char Mips16HardFloat::ID = 0;
57 // Return types that matter for hard float are:
58 // float, double, complex float, and complex double
60 enum FPReturnVariant {
61 FRet, DRet, CFRet, CDRet, NoFPRet
65 // Determine which FP return type this function has
67 static FPReturnVariant whichFPReturnVariant(Type *T) {
68 switch (T->getTypeID()) {
71 case Type::DoubleTyID:
73 case Type::StructTyID:
74 if (T->getStructNumElements() != 2)
76 if ((T->getContainedType(0)->isFloatTy()) &&
77 (T->getContainedType(1)->isFloatTy()))
79 if ((T->getContainedType(0)->isDoubleTy()) &&
80 (T->getContainedType(1)->isDoubleTy()))
90 // Parameter type that matter are float, (float, float), (float, double),
91 // double, (double, double), (double, float)
95 DSig, DDSig, DFSig, NoSig
98 // which floating point parameter signature variant we are dealing with
100 typedef Type::TypeID TypeID;
101 const Type::TypeID FloatTyID = Type::FloatTyID;
102 const Type::TypeID DoubleTyID = Type::DoubleTyID;
104 static FPParamVariant whichFPParamVariantNeeded(Function &F) {
105 switch (F.arg_size()) {
109 TypeID ArgTypeID = F.getFunctionType()->getParamType(0)->getTypeID();
120 TypeID ArgTypeID0 = F.getFunctionType()->getParamType(0)->getTypeID();
121 TypeID ArgTypeID1 = F.getFunctionType()->getParamType(1)->getTypeID();
124 switch (ArgTypeID1) {
134 switch (ArgTypeID1) {
148 llvm_unreachable("can't get here");
151 // Figure out if we need float point based on the function parameters.
152 // We need to move variables in and/or out of floating point
153 // registers because of the ABI
155 static bool needsFPStubFromParams(Function &F) {
156 if (F.arg_size() >=1) {
157 Type *ArgType = F.getFunctionType()->getParamType(0);
158 switch (ArgType->getTypeID()) {
159 case Type::FloatTyID:
160 case Type::DoubleTyID:
169 static bool needsFPReturnHelper(Function &F) {
170 Type* RetType = F.getReturnType();
171 return whichFPReturnVariant(RetType) != NoFPRet;
174 static bool needsFPReturnHelper(FunctionType &FT) {
175 Type* RetType = FT.getReturnType();
176 return whichFPReturnVariant(RetType) != NoFPRet;
179 static bool needsFPHelperFromSig(Function &F) {
180 return needsFPStubFromParams(F) || needsFPReturnHelper(F);
184 // We swap between FP and Integer registers to allow Mips16 and Mips32 to
187 static std::string swapFPIntParams(FPParamVariant PV, Module *M, bool LE,
189 std::string MI = ToFP ? "mtc1 ": "mfc1 ";
194 AsmText += MI + "$$4, $$f12\n";
198 AsmText += MI + "$$4, $$f12\n";
199 AsmText += MI + "$$5, $$f14\n";
203 AsmText += MI + "$$4, $$f12\n";
205 AsmText += MI + "$$6, $$f14\n";
206 AsmText += MI + "$$7, $$f15\n";
208 AsmText += MI + "$$7, $$f14\n";
209 AsmText += MI + "$$6, $$f15\n";
215 AsmText += MI + "$$4, $$f12\n";
216 AsmText += MI + "$$5, $$f13\n";
218 AsmText += MI + "$$5, $$f12\n";
219 AsmText += MI + "$$4, $$f13\n";
225 AsmText += MI + "$$4, $$f12\n";
226 AsmText += MI + "$$5, $$f13\n";
227 AsmText += MI + "$$6, $$f14\n";
228 AsmText += MI + "$$7, $$f15\n";
230 AsmText += MI + "$$5, $$f12\n";
231 AsmText += MI + "$$4, $$f13\n";
232 AsmText += MI + "$$7, $$f14\n";
233 AsmText += MI + "$$6, $$f15\n";
239 AsmText += MI + "$$4, $$f12\n";
240 AsmText += MI + "$$5, $$f13\n";
242 AsmText += MI + "$$5, $$f12\n";
243 AsmText += MI + "$$4, $$f13\n";
245 AsmText += MI + "$$6, $$f14\n";
256 // Make sure that we know we already need a stub for this function.
257 // Having called needsFPHelperFromSig
259 static void assureFPCallStub(Function &F, Module *M,
260 const MipsTargetMachine &TM) {
261 // for now we only need them for static relocation
262 if (TM.isPositionIndependent())
264 LLVMContext &Context = M->getContext();
265 bool LE = TM.isLittleEndian();
266 std::string Name = F.getName();
267 std::string SectionName = ".mips16.call.fp." + Name;
268 std::string StubName = "__call_stub_fp_" + Name;
270 // see if we already have the stub
272 Function *FStub = M->getFunction(StubName);
273 if (FStub && !FStub->isDeclaration()) return;
274 FStub = Function::Create(F.getFunctionType(),
275 Function::InternalLinkage, StubName, M);
276 FStub->addFnAttr("mips16_fp_stub");
277 FStub->addFnAttr(llvm::Attribute::Naked);
278 FStub->addFnAttr(llvm::Attribute::NoInline);
279 FStub->addFnAttr(llvm::Attribute::NoUnwind);
280 FStub->addFnAttr("nomips16");
281 FStub->setSection(SectionName);
282 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
283 FPReturnVariant RV = whichFPReturnVariant(FStub->getReturnType());
284 FPParamVariant PV = whichFPParamVariantNeeded(F);
287 AsmText += ".set reorder\n";
288 AsmText += swapFPIntParams(PV, M, LE, true);
290 AsmText += "move $$18, $$31\n";
291 AsmText += "jal " + Name + "\n";
293 AsmText += "lui $$25, %hi(" + Name + ")\n";
294 AsmText += "addiu $$25, $$25, %lo(" + Name + ")\n";
299 AsmText += "mfc1 $$2, $$f0\n";
304 AsmText += "mfc1 $$2, $$f0\n";
305 AsmText += "mfc1 $$3, $$f1\n";
307 AsmText += "mfc1 $$3, $$f0\n";
308 AsmText += "mfc1 $$2, $$f1\n";
314 AsmText += "mfc1 $$2, $$f0\n";
315 AsmText += "mfc1 $$3, $$f2\n";
317 AsmText += "mfc1 $$3, $$f0\n";
318 AsmText += "mfc1 $$3, $$f2\n";
324 AsmText += "mfc1 $$4, $$f2\n";
325 AsmText += "mfc1 $$5, $$f3\n";
326 AsmText += "mfc1 $$2, $$f0\n";
327 AsmText += "mfc1 $$3, $$f1\n";
330 AsmText += "mfc1 $$5, $$f2\n";
331 AsmText += "mfc1 $$4, $$f3\n";
332 AsmText += "mfc1 $$3, $$f0\n";
333 AsmText += "mfc1 $$2, $$f1\n";
342 AsmText += "jr $$18\n";
344 AsmText += "jr $$25\n";
345 EmitInlineAsm(Context, BB, AsmText);
347 new UnreachableInst(Context, BB);
351 // Functions that are llvm intrinsics and don't need helpers.
353 static const char *const IntrinsicInline[] = {
355 "llvm.ceil.f32", "llvm.ceil.f64",
356 "llvm.copysign.f32", "llvm.copysign.f64",
357 "llvm.cos.f32", "llvm.cos.f64",
358 "llvm.exp.f32", "llvm.exp.f64",
359 "llvm.exp2.f32", "llvm.exp2.f64",
360 "llvm.fabs.f32", "llvm.fabs.f64",
361 "llvm.floor.f32", "llvm.floor.f64",
362 "llvm.fma.f32", "llvm.fma.f64",
363 "llvm.log.f32", "llvm.log.f64",
364 "llvm.log10.f32", "llvm.log10.f64",
365 "llvm.nearbyint.f32", "llvm.nearbyint.f64",
366 "llvm.pow.f32", "llvm.pow.f64",
367 "llvm.powi.f32", "llvm.powi.f64",
368 "llvm.rint.f32", "llvm.rint.f64",
369 "llvm.round.f32", "llvm.round.f64",
370 "llvm.sin.f32", "llvm.sin.f64",
371 "llvm.sqrt.f32", "llvm.sqrt.f64",
372 "llvm.trunc.f32", "llvm.trunc.f64",
375 static bool isIntrinsicInline(Function *F) {
376 return std::binary_search(std::begin(IntrinsicInline),
377 std::end(IntrinsicInline), F->getName());
380 // Returns of float, double and complex need to be handled with a helper
383 static bool fixupFPReturnAndCall(Function &F, Module *M,
384 const MipsTargetMachine &TM) {
385 bool Modified = false;
386 LLVMContext &C = M->getContext();
387 Type *MyVoid = Type::getVoidTy(C);
390 if (const ReturnInst *RI = dyn_cast<ReturnInst>(&I)) {
391 Value *RVal = RI->getReturnValue();
394 // If there is a return value and it needs a helper function,
395 // figure out which one and add a call before the actual
396 // return to this helper. The purpose of the helper is to move
397 // floating point values from their soft float return mapping to
398 // where they would have been mapped to in floating point registers.
400 Type *T = RVal->getType();
401 FPReturnVariant RV = whichFPReturnVariant(T);
402 if (RV == NoFPRet) continue;
403 static const char *const Helper[NoFPRet] = {
404 "__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",
407 const char *Name = Helper[RV];
409 Value *Params[] = {RVal};
412 // These helper functions have a different calling ABI so
413 // this __Mips16RetHelper indicates that so that later
414 // during call setup, the proper call lowering to the helper
415 // functions will take place.
417 A = A.addAttribute(C, AttributeSet::FunctionIndex,
418 "__Mips16RetHelper");
419 A = A.addAttribute(C, AttributeSet::FunctionIndex,
420 Attribute::ReadNone);
421 A = A.addAttribute(C, AttributeSet::FunctionIndex,
422 Attribute::NoInline);
423 Value *F = (M->getOrInsertFunction(Name, A, MyVoid, T, nullptr));
424 CallInst::Create(F, Params, "", &I);
425 } else if (const CallInst *CI = dyn_cast<CallInst>(&I)) {
426 FunctionType *FT = CI->getFunctionType();
427 Function *F_ = CI->getCalledFunction();
428 if (needsFPReturnHelper(*FT) &&
429 !(F_ && isIntrinsicInline(F_))) {
431 F.addFnAttr("saveS2");
433 if (F_ && !isIntrinsicInline(F_)) {
434 // pic mode calls are handled by already defined
436 if (needsFPReturnHelper(*F_)) {
438 F.addFnAttr("saveS2");
440 if (!TM.isPositionIndependent()) {
441 if (needsFPHelperFromSig(*F_)) {
442 assureFPCallStub(*F_, M, TM);
452 static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,
453 const MipsTargetMachine &TM) {
454 bool PicMode = TM.isPositionIndependent();
455 bool LE = TM.isLittleEndian();
456 LLVMContext &Context = M->getContext();
457 std::string Name = F->getName();
458 std::string SectionName = ".mips16.fn." + Name;
459 std::string StubName = "__fn_stub_" + Name;
460 std::string LocalName = "$$__fn_local_" + Name;
461 Function *FStub = Function::Create
462 (F->getFunctionType(),
463 Function::InternalLinkage, StubName, M);
464 FStub->addFnAttr("mips16_fp_stub");
465 FStub->addFnAttr(llvm::Attribute::Naked);
466 FStub->addFnAttr(llvm::Attribute::NoUnwind);
467 FStub->addFnAttr(llvm::Attribute::NoInline);
468 FStub->addFnAttr("nomips16");
469 FStub->setSection(SectionName);
470 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
474 AsmText += ".set noreorder\n";
475 AsmText += ".cpload $$25\n";
476 AsmText += ".set reorder\n";
477 AsmText += ".reloc 0, R_MIPS_NONE, " + Name + "\n";
478 AsmText += "la $$25, " + LocalName + "\n";
480 AsmText += "la $$25, " + Name + "\n";
481 AsmText += swapFPIntParams(PV, M, LE, false);
482 AsmText += "jr $$25\n";
483 AsmText += LocalName + " = " + Name + "\n";
484 EmitInlineAsm(Context, BB, AsmText);
486 new UnreachableInst(FStub->getContext(), BB);
490 // remove the use-soft-float attribute
492 static void removeUseSoftFloat(Function &F) {
494 DEBUG(errs() << "removing -use-soft-float\n");
495 A = A.addAttribute(F.getContext(), AttributeSet::FunctionIndex,
496 "use-soft-float", "false");
497 F.removeAttributes(AttributeSet::FunctionIndex, A);
498 if (F.hasFnAttribute("use-soft-float")) {
499 DEBUG(errs() << "still has -use-soft-float\n");
501 F.addAttributes(AttributeSet::FunctionIndex, A);
506 // This pass only makes sense when the underlying chip has floating point but
507 // we are compiling as mips16.
508 // For all mips16 functions (that are not stubs we have already generated), or
509 // declared via attributes as nomips16, we must:
510 // 1) fixup all returns of float, double, single and double complex
511 // by calling a helper function before the actual return.
512 // 2) generate helper functions (stubs) that can be called by mips32
513 // functions that will move parameters passed normally passed in
515 // registers the soft float equivalents.
516 // 3) in the case of static relocation, generate helper functions so that
517 // mips16 functions can call extern functions of unknown type (mips16 or
519 // 4) TBD. For pic, calls to extern functions of unknown type are handled by
520 // predefined helper functions in libc but this work is currently done
521 // during call lowering but it should be moved here in the future.
523 bool Mips16HardFloat::runOnModule(Module &M) {
524 DEBUG(errs() << "Run on Module Mips16HardFloat\n");
525 bool Modified = false;
526 for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) {
527 if (F->hasFnAttribute("nomips16") &&
528 F->hasFnAttribute("use-soft-float")) {
529 removeUseSoftFloat(*F);
532 if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
533 F->hasFnAttribute("nomips16")) continue;
534 Modified |= fixupFPReturnAndCall(*F, &M, TM);
535 FPParamVariant V = whichFPParamVariantNeeded(*F);
538 createFPFnStub(&*F, &M, V, TM);
545 ModulePass *llvm::createMips16HardFloatPass(MipsTargetMachine &TM) {
546 return new Mips16HardFloat(TM);