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 const char *getPassName() const override {
34 return "MIPS16 Hard Float Pass";
37 bool runOnModule(Module &M) override;
40 const MipsTargetMachine &TM;
43 class InlineAsmHelper {
47 InlineAsmHelper(LLVMContext &C_, BasicBlock *BB_) :
51 void Out(StringRef AsmString) {
52 std::vector<llvm::Type *> AsmArgTypes;
53 std::vector<llvm::Value*> AsmArgs;
55 llvm::FunctionType *AsmFTy = llvm::FunctionType::get(Type::getVoidTy(C),
57 llvm::InlineAsm *IA = llvm::InlineAsm::get(AsmFTy, AsmString, "", true,
58 /* IsAlignStack */ false,
59 llvm::InlineAsm::AD_ATT);
60 CallInst::Create(IA, AsmArgs, "", BB);
64 char Mips16HardFloat::ID = 0;
68 // Return types that matter for hard float are:
69 // float, double, complex float, and complex double
71 enum FPReturnVariant {
72 FRet, DRet, CFRet, CDRet, NoFPRet
76 // Determine which FP return type this function has
78 static FPReturnVariant whichFPReturnVariant(Type *T) {
79 switch (T->getTypeID()) {
82 case Type::DoubleTyID:
84 case Type::StructTyID:
85 if (T->getStructNumElements() != 2)
87 if ((T->getContainedType(0)->isFloatTy()) &&
88 (T->getContainedType(1)->isFloatTy()))
90 if ((T->getContainedType(0)->isDoubleTy()) &&
91 (T->getContainedType(1)->isDoubleTy()))
101 // Parameter type that matter are float, (float, float), (float, double),
102 // double, (double, double), (double, float)
104 enum FPParamVariant {
106 DSig, DDSig, DFSig, NoSig
109 // which floating point parameter signature variant we are dealing with
111 typedef Type::TypeID TypeID;
112 const Type::TypeID FloatTyID = Type::FloatTyID;
113 const Type::TypeID DoubleTyID = Type::DoubleTyID;
115 static FPParamVariant whichFPParamVariantNeeded(Function &F) {
116 switch (F.arg_size()) {
120 TypeID ArgTypeID = F.getFunctionType()->getParamType(0)->getTypeID();
131 TypeID ArgTypeID0 = F.getFunctionType()->getParamType(0)->getTypeID();
132 TypeID ArgTypeID1 = F.getFunctionType()->getParamType(1)->getTypeID();
135 switch (ArgTypeID1) {
145 switch (ArgTypeID1) {
159 llvm_unreachable("can't get here");
162 // Figure out if we need float point based on the function parameters.
163 // We need to move variables in and/or out of floating point
164 // registers because of the ABI
166 static bool needsFPStubFromParams(Function &F) {
167 if (F.arg_size() >=1) {
168 Type *ArgType = F.getFunctionType()->getParamType(0);
169 switch (ArgType->getTypeID()) {
170 case Type::FloatTyID:
171 case Type::DoubleTyID:
180 static bool needsFPReturnHelper(Function &F) {
181 Type* RetType = F.getReturnType();
182 return whichFPReturnVariant(RetType) != NoFPRet;
185 static bool needsFPReturnHelper(FunctionType &FT) {
186 Type* RetType = FT.getReturnType();
187 return whichFPReturnVariant(RetType) != NoFPRet;
190 static bool needsFPHelperFromSig(Function &F) {
191 return needsFPStubFromParams(F) || needsFPReturnHelper(F);
195 // We swap between FP and Integer registers to allow Mips16 and Mips32 to
198 static void swapFPIntParams(FPParamVariant PV, Module *M, InlineAsmHelper &IAH,
199 bool LE, bool ToFP) {
200 //LLVMContext &Context = M->getContext();
201 std::string MI = ToFP? "mtc1 ": "mfc1 ";
204 IAH.Out(MI + "$$4,$$f12");
207 IAH.Out(MI +"$$4,$$f12");
208 IAH.Out(MI + "$$5,$$f14");
211 IAH.Out(MI + "$$4,$$f12");
213 IAH.Out(MI + "$$6,$$f14");
214 IAH.Out(MI + "$$7,$$f15");
216 IAH.Out(MI + "$$7,$$f14");
217 IAH.Out(MI + "$$6,$$f15");
222 IAH.Out(MI + "$$4,$$f12");
223 IAH.Out(MI + "$$5,$$f13");
225 IAH.Out(MI + "$$5,$$f12");
226 IAH.Out(MI + "$$4,$$f13");
231 IAH.Out(MI + "$$4,$$f12");
232 IAH.Out(MI + "$$5,$$f13");
233 IAH.Out(MI + "$$6,$$f14");
234 IAH.Out(MI + "$$7,$$f15");
236 IAH.Out(MI + "$$5,$$f12");
237 IAH.Out(MI + "$$4,$$f13");
238 IAH.Out(MI + "$$7,$$f14");
239 IAH.Out(MI + "$$6,$$f15");
244 IAH.Out(MI + "$$4,$$f12");
245 IAH.Out(MI + "$$5,$$f13");
247 IAH.Out(MI + "$$5,$$f12");
248 IAH.Out(MI + "$$4,$$f13");
250 IAH.Out(MI + "$$6,$$f14");
258 // Make sure that we know we already need a stub for this function.
259 // Having called needsFPHelperFromSig
261 static void assureFPCallStub(Function &F, Module *M,
262 const MipsTargetMachine &TM) {
263 // for now we only need them for static relocation
264 if (TM.getRelocationModel() == Reloc::PIC_)
266 LLVMContext &Context = M->getContext();
267 bool LE = TM.isLittleEndian();
268 std::string Name = F.getName();
269 std::string SectionName = ".mips16.call.fp." + Name;
270 std::string StubName = "__call_stub_fp_" + Name;
272 // see if we already have the stub
274 Function *FStub = M->getFunction(StubName);
275 if (FStub && !FStub->isDeclaration()) return;
276 FStub = Function::Create(F.getFunctionType(),
277 Function::InternalLinkage, StubName, M);
278 FStub->addFnAttr("mips16_fp_stub");
279 FStub->addFnAttr(llvm::Attribute::Naked);
280 FStub->addFnAttr(llvm::Attribute::NoInline);
281 FStub->addFnAttr(llvm::Attribute::NoUnwind);
282 FStub->addFnAttr("nomips16");
283 FStub->setSection(SectionName);
284 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
285 InlineAsmHelper IAH(Context, BB);
286 IAH.Out(".set reorder");
287 FPReturnVariant RV = whichFPReturnVariant(FStub->getReturnType());
288 FPParamVariant PV = whichFPParamVariantNeeded(F);
289 swapFPIntParams(PV, M, IAH, LE, true);
291 IAH.Out("move $$18, $$31");
292 IAH.Out("jal " + Name);
294 IAH.Out("lui $$25,%hi(" + Name + ")");
295 IAH.Out("addiu $$25,$$25,%lo(" + Name + ")" );
299 IAH.Out("mfc1 $$2,$$f0");
303 IAH.Out("mfc1 $$2,$$f0");
304 IAH.Out("mfc1 $$3,$$f1");
306 IAH.Out("mfc1 $$3,$$f0");
307 IAH.Out("mfc1 $$2,$$f1");
312 IAH.Out("mfc1 $$2,$$f0");
313 IAH.Out("mfc1 $$3,$$f2");
315 IAH.Out("mfc1 $$3,$$f0");
316 IAH.Out("mfc1 $$3,$$f2");
321 IAH.Out("mfc1 $$4,$$f2");
322 IAH.Out("mfc1 $$5,$$f3");
323 IAH.Out("mfc1 $$2,$$f0");
324 IAH.Out("mfc1 $$3,$$f1");
327 IAH.Out("mfc1 $$5,$$f2");
328 IAH.Out("mfc1 $$4,$$f3");
329 IAH.Out("mfc1 $$3,$$f0");
330 IAH.Out("mfc1 $$2,$$f1");
340 new UnreachableInst(Context, BB);
344 // Functions that are llvm intrinsics and don't need helpers.
346 static const char *const IntrinsicInline[] = {
348 "llvm.ceil.f32", "llvm.ceil.f64",
349 "llvm.copysign.f32", "llvm.copysign.f64",
350 "llvm.cos.f32", "llvm.cos.f64",
351 "llvm.exp.f32", "llvm.exp.f64",
352 "llvm.exp2.f32", "llvm.exp2.f64",
353 "llvm.fabs.f32", "llvm.fabs.f64",
354 "llvm.floor.f32", "llvm.floor.f64",
355 "llvm.fma.f32", "llvm.fma.f64",
356 "llvm.log.f32", "llvm.log.f64",
357 "llvm.log10.f32", "llvm.log10.f64",
358 "llvm.nearbyint.f32", "llvm.nearbyint.f64",
359 "llvm.pow.f32", "llvm.pow.f64",
360 "llvm.powi.f32", "llvm.powi.f64",
361 "llvm.rint.f32", "llvm.rint.f64",
362 "llvm.round.f32", "llvm.round.f64",
363 "llvm.sin.f32", "llvm.sin.f64",
364 "llvm.sqrt.f32", "llvm.sqrt.f64",
365 "llvm.trunc.f32", "llvm.trunc.f64",
368 static bool isIntrinsicInline(Function *F) {
369 return std::binary_search(std::begin(IntrinsicInline),
370 std::end(IntrinsicInline), F->getName());
373 // Returns of float, double and complex need to be handled with a helper
376 static bool fixupFPReturnAndCall(Function &F, Module *M,
377 const MipsTargetMachine &TM) {
378 bool Modified = false;
379 LLVMContext &C = M->getContext();
380 Type *MyVoid = Type::getVoidTy(C);
381 for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB)
382 for (BasicBlock::iterator I = BB->begin(), E = BB->end();
384 Instruction &Inst = *I;
385 if (const ReturnInst *RI = dyn_cast<ReturnInst>(I)) {
386 Value *RVal = RI->getReturnValue();
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.
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",
402 const char *Name = Helper[RV];
404 Value *Params[] = {RVal};
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.
412 A = A.addAttribute(C, AttributeSet::FunctionIndex,
413 "__Mips16RetHelper");
414 A = A.addAttribute(C, AttributeSet::FunctionIndex,
415 Attribute::ReadNone);
416 A = A.addAttribute(C, AttributeSet::FunctionIndex,
417 Attribute::NoInline);
418 Value *F = (M->getOrInsertFunction(Name, A, MyVoid, T, nullptr));
419 CallInst::Create(F, Params, "", &Inst );
420 } else if (const CallInst *CI = dyn_cast<CallInst>(I)) {
421 const Value* V = CI->getCalledValue();
423 if (V) T = V->getType();
424 PointerType *PFT = nullptr;
425 if (T) PFT = dyn_cast<PointerType>(T);
426 FunctionType *FT = nullptr;
427 if (PFT) FT = dyn_cast<FunctionType>(PFT->getElementType());
428 Function *F_ = CI->getCalledFunction();
429 if (FT && needsFPReturnHelper(*FT) &&
430 !(F_ && isIntrinsicInline(F_))) {
432 F.addFnAttr("saveS2");
434 if (F_ && !isIntrinsicInline(F_)) {
435 // pic mode calls are handled by already defined
437 if (needsFPReturnHelper(*F_)) {
439 F.addFnAttr("saveS2");
441 if (TM.getRelocationModel() != Reloc::PIC_ ) {
442 if (needsFPHelperFromSig(*F_)) {
443 assureFPCallStub(*F_, M, TM);
453 static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,
454 const MipsTargetMachine &TM) {
455 bool PicMode = TM.getRelocationModel() == Reloc::PIC_;
456 bool LE = TM.isLittleEndian();
457 LLVMContext &Context = M->getContext();
458 std::string Name = F->getName();
459 std::string SectionName = ".mips16.fn." + Name;
460 std::string StubName = "__fn_stub_" + Name;
461 std::string LocalName = "$$__fn_local_" + Name;
462 Function *FStub = Function::Create
463 (F->getFunctionType(),
464 Function::InternalLinkage, StubName, M);
465 FStub->addFnAttr("mips16_fp_stub");
466 FStub->addFnAttr(llvm::Attribute::Naked);
467 FStub->addFnAttr(llvm::Attribute::NoUnwind);
468 FStub->addFnAttr(llvm::Attribute::NoInline);
469 FStub->addFnAttr("nomips16");
470 FStub->setSection(SectionName);
471 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
472 InlineAsmHelper IAH(Context, BB);
474 IAH.Out(".set noreorder");
475 IAH.Out(".cpload $$25");
476 IAH.Out(".set reorder");
477 IAH.Out(".reloc 0,R_MIPS_NONE," + Name);
478 IAH.Out("la $$25," + LocalName);
481 IAH.Out("la $$25," + Name);
483 swapFPIntParams(PV, M, IAH, LE, false);
485 IAH.Out(LocalName + " = " + Name);
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);