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 static void EmitInlineAsm(LLVMContext &C, BasicBlock *BB, StringRef AsmText) {
44 std::vector<llvm::Type *> AsmArgTypes;
45 std::vector<llvm::Value *> AsmArgs;
47 llvm::FunctionType *AsmFTy =
48 llvm::FunctionType::get(Type::getVoidTy(C), AsmArgTypes, false);
50 llvm::InlineAsm::get(AsmFTy, AsmText, "", true,
51 /* IsAlignStack */ false, llvm::InlineAsm::AD_ATT);
52 CallInst::Create(IA, AsmArgs, "", BB);
55 char Mips16HardFloat::ID = 0;
59 // Return types that matter for hard float are:
60 // float, double, complex float, and complex double
62 enum FPReturnVariant {
63 FRet, DRet, CFRet, CDRet, NoFPRet
67 // Determine which FP return type this function has
69 static FPReturnVariant whichFPReturnVariant(Type *T) {
70 switch (T->getTypeID()) {
73 case Type::DoubleTyID:
75 case Type::StructTyID:
76 if (T->getStructNumElements() != 2)
78 if ((T->getContainedType(0)->isFloatTy()) &&
79 (T->getContainedType(1)->isFloatTy()))
81 if ((T->getContainedType(0)->isDoubleTy()) &&
82 (T->getContainedType(1)->isDoubleTy()))
92 // Parameter type that matter are float, (float, float), (float, double),
93 // double, (double, double), (double, float)
97 DSig, DDSig, DFSig, NoSig
100 // which floating point parameter signature variant we are dealing with
102 typedef Type::TypeID TypeID;
103 const Type::TypeID FloatTyID = Type::FloatTyID;
104 const Type::TypeID DoubleTyID = Type::DoubleTyID;
106 static FPParamVariant whichFPParamVariantNeeded(Function &F) {
107 switch (F.arg_size()) {
111 TypeID ArgTypeID = F.getFunctionType()->getParamType(0)->getTypeID();
122 TypeID ArgTypeID0 = F.getFunctionType()->getParamType(0)->getTypeID();
123 TypeID ArgTypeID1 = F.getFunctionType()->getParamType(1)->getTypeID();
126 switch (ArgTypeID1) {
136 switch (ArgTypeID1) {
150 llvm_unreachable("can't get here");
153 // Figure out if we need float point based on the function parameters.
154 // We need to move variables in and/or out of floating point
155 // 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:
171 static bool needsFPReturnHelper(Function &F) {
172 Type* RetType = F.getReturnType();
173 return whichFPReturnVariant(RetType) != NoFPRet;
176 static bool needsFPReturnHelper(FunctionType &FT) {
177 Type* RetType = FT.getReturnType();
178 return whichFPReturnVariant(RetType) != NoFPRet;
181 static bool needsFPHelperFromSig(Function &F) {
182 return needsFPStubFromParams(F) || needsFPReturnHelper(F);
186 // We swap between FP and Integer registers to allow Mips16 and Mips32 to
189 static std::string swapFPIntParams(FPParamVariant PV, Module *M, bool LE,
191 std::string MI = ToFP ? "mtc1 ": "mfc1 ";
196 AsmText += MI + "$$4, $$f12\n";
200 AsmText += MI + "$$4, $$f12\n";
201 AsmText += MI + "$$5, $$f14\n";
205 AsmText += MI + "$$4, $$f12\n";
207 AsmText += MI + "$$6, $$f14\n";
208 AsmText += MI + "$$7, $$f15\n";
210 AsmText += MI + "$$7, $$f14\n";
211 AsmText += MI + "$$6, $$f15\n";
217 AsmText += MI + "$$4, $$f12\n";
218 AsmText += MI + "$$5, $$f13\n";
220 AsmText += MI + "$$5, $$f12\n";
221 AsmText += MI + "$$4, $$f13\n";
227 AsmText += MI + "$$4, $$f12\n";
228 AsmText += MI + "$$5, $$f13\n";
229 AsmText += MI + "$$6, $$f14\n";
230 AsmText += MI + "$$7, $$f15\n";
232 AsmText += MI + "$$5, $$f12\n";
233 AsmText += MI + "$$4, $$f13\n";
234 AsmText += MI + "$$7, $$f14\n";
235 AsmText += MI + "$$6, $$f15\n";
241 AsmText += MI + "$$4, $$f12\n";
242 AsmText += MI + "$$5, $$f13\n";
244 AsmText += MI + "$$5, $$f12\n";
245 AsmText += MI + "$$4, $$f13\n";
247 AsmText += MI + "$$6, $$f14\n";
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.isPositionIndependent())
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 FPReturnVariant RV = whichFPReturnVariant(FStub->getReturnType());
286 FPParamVariant PV = whichFPParamVariantNeeded(F);
289 AsmText += ".set reorder\n";
290 AsmText += swapFPIntParams(PV, M, LE, true);
292 AsmText += "move $$18, $$31\n";
293 AsmText += "jal " + Name + "\n";
295 AsmText += "lui $$25, %hi(" + Name + ")\n";
296 AsmText += "addiu $$25, $$25, %lo(" + Name + ")\n";
301 AsmText += "mfc1 $$2, $$f0\n";
306 AsmText += "mfc1 $$2, $$f0\n";
307 AsmText += "mfc1 $$3, $$f1\n";
309 AsmText += "mfc1 $$3, $$f0\n";
310 AsmText += "mfc1 $$2, $$f1\n";
316 AsmText += "mfc1 $$2, $$f0\n";
317 AsmText += "mfc1 $$3, $$f2\n";
319 AsmText += "mfc1 $$3, $$f0\n";
320 AsmText += "mfc1 $$3, $$f2\n";
326 AsmText += "mfc1 $$4, $$f2\n";
327 AsmText += "mfc1 $$5, $$f3\n";
328 AsmText += "mfc1 $$2, $$f0\n";
329 AsmText += "mfc1 $$3, $$f1\n";
332 AsmText += "mfc1 $$5, $$f2\n";
333 AsmText += "mfc1 $$4, $$f3\n";
334 AsmText += "mfc1 $$3, $$f0\n";
335 AsmText += "mfc1 $$2, $$f1\n";
344 AsmText += "jr $$18\n";
346 AsmText += "jr $$25\n";
347 EmitInlineAsm(Context, BB, AsmText);
349 new UnreachableInst(Context, BB);
353 // Functions that are llvm intrinsics and don't need helpers.
355 static const char *const IntrinsicInline[] = {
357 "llvm.ceil.f32", "llvm.ceil.f64",
358 "llvm.copysign.f32", "llvm.copysign.f64",
359 "llvm.cos.f32", "llvm.cos.f64",
360 "llvm.exp.f32", "llvm.exp.f64",
361 "llvm.exp2.f32", "llvm.exp2.f64",
362 "llvm.fabs.f32", "llvm.fabs.f64",
363 "llvm.floor.f32", "llvm.floor.f64",
364 "llvm.fma.f32", "llvm.fma.f64",
365 "llvm.log.f32", "llvm.log.f64",
366 "llvm.log10.f32", "llvm.log10.f64",
367 "llvm.nearbyint.f32", "llvm.nearbyint.f64",
368 "llvm.pow.f32", "llvm.pow.f64",
369 "llvm.powi.f32", "llvm.powi.f64",
370 "llvm.rint.f32", "llvm.rint.f64",
371 "llvm.round.f32", "llvm.round.f64",
372 "llvm.sin.f32", "llvm.sin.f64",
373 "llvm.sqrt.f32", "llvm.sqrt.f64",
374 "llvm.trunc.f32", "llvm.trunc.f64",
377 static bool isIntrinsicInline(Function *F) {
378 return std::binary_search(std::begin(IntrinsicInline),
379 std::end(IntrinsicInline), F->getName());
382 // Returns of float, double and complex need to be handled with a helper
385 static bool fixupFPReturnAndCall(Function &F, Module *M,
386 const MipsTargetMachine &TM) {
387 bool Modified = false;
388 LLVMContext &C = M->getContext();
389 Type *MyVoid = Type::getVoidTy(C);
392 if (const ReturnInst *RI = dyn_cast<ReturnInst>(&I)) {
393 Value *RVal = RI->getReturnValue();
396 // If there is a return value and it needs a helper function,
397 // figure out which one and add a call before the actual
398 // return to this helper. The purpose of the helper is to move
399 // floating point values from their soft float return mapping to
400 // where they would have been mapped to in floating point registers.
402 Type *T = RVal->getType();
403 FPReturnVariant RV = whichFPReturnVariant(T);
404 if (RV == NoFPRet) continue;
405 static const char *const Helper[NoFPRet] = {
406 "__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",
409 const char *Name = Helper[RV];
411 Value *Params[] = {RVal};
414 // These helper functions have a different calling ABI so
415 // this __Mips16RetHelper indicates that so that later
416 // during call setup, the proper call lowering to the helper
417 // functions will take place.
419 A = A.addAttribute(C, AttributeSet::FunctionIndex,
420 "__Mips16RetHelper");
421 A = A.addAttribute(C, AttributeSet::FunctionIndex,
422 Attribute::ReadNone);
423 A = A.addAttribute(C, AttributeSet::FunctionIndex,
424 Attribute::NoInline);
425 Value *F = (M->getOrInsertFunction(Name, A, MyVoid, T, nullptr));
426 CallInst::Create(F, Params, "", &I);
427 } else if (const CallInst *CI = dyn_cast<CallInst>(&I)) {
428 FunctionType *FT = CI->getFunctionType();
429 Function *F_ = CI->getCalledFunction();
430 if (needsFPReturnHelper(*FT) &&
431 !(F_ && isIntrinsicInline(F_))) {
433 F.addFnAttr("saveS2");
435 if (F_ && !isIntrinsicInline(F_)) {
436 // pic mode calls are handled by already defined
438 if (needsFPReturnHelper(*F_)) {
440 F.addFnAttr("saveS2");
442 if (!TM.isPositionIndependent()) {
443 if (needsFPHelperFromSig(*F_)) {
444 assureFPCallStub(*F_, M, TM);
454 static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,
455 const MipsTargetMachine &TM) {
456 bool PicMode = TM.isPositionIndependent();
457 bool LE = TM.isLittleEndian();
458 LLVMContext &Context = M->getContext();
459 std::string Name = F->getName();
460 std::string SectionName = ".mips16.fn." + Name;
461 std::string StubName = "__fn_stub_" + Name;
462 std::string LocalName = "$$__fn_local_" + Name;
463 Function *FStub = Function::Create
464 (F->getFunctionType(),
465 Function::InternalLinkage, StubName, M);
466 FStub->addFnAttr("mips16_fp_stub");
467 FStub->addFnAttr(llvm::Attribute::Naked);
468 FStub->addFnAttr(llvm::Attribute::NoUnwind);
469 FStub->addFnAttr(llvm::Attribute::NoInline);
470 FStub->addFnAttr("nomips16");
471 FStub->setSection(SectionName);
472 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
476 AsmText += ".set noreorder\n";
477 AsmText += ".cpload $$25\n";
478 AsmText += ".set reorder\n";
479 AsmText += ".reloc 0, R_MIPS_NONE, " + Name + "\n";
480 AsmText += "la $$25, " + LocalName + "\n";
482 AsmText += "la $$25, " + Name + "\n";
483 AsmText += swapFPIntParams(PV, M, LE, false);
484 AsmText += "jr $$25\n";
485 AsmText += LocalName + " = " + Name + "\n";
486 EmitInlineAsm(Context, BB, AsmText);
488 new UnreachableInst(FStub->getContext(), BB);
492 // remove the use-soft-float attribute
494 static void removeUseSoftFloat(Function &F) {
496 DEBUG(errs() << "removing -use-soft-float\n");
497 A = A.addAttribute(F.getContext(), AttributeSet::FunctionIndex,
498 "use-soft-float", "false");
499 F.removeAttributes(AttributeSet::FunctionIndex, A);
500 if (F.hasFnAttribute("use-soft-float")) {
501 DEBUG(errs() << "still has -use-soft-float\n");
503 F.addAttributes(AttributeSet::FunctionIndex, A);
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
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
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.
525 bool Mips16HardFloat::runOnModule(Module &M) {
526 DEBUG(errs() << "Run on Module Mips16HardFloat\n");
527 bool Modified = false;
528 for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) {
529 if (F->hasFnAttribute("nomips16") &&
530 F->hasFnAttribute("use-soft-float")) {
531 removeUseSoftFloat(*F);
534 if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
535 F->hasFnAttribute("nomips16")) continue;
536 Modified |= fixupFPReturnAndCall(*F, &M, TM);
537 FPParamVariant V = whichFPParamVariantNeeded(*F);
540 createFPFnStub(&*F, &M, V, TM);
547 ModulePass *llvm::createMips16HardFloatPass(MipsTargetMachine &TM) {
548 return new Mips16HardFloat(TM);