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