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