LLVM 22.0.0git
Mips16HardFloat.cpp
Go to the documentation of this file.
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"
15#include "llvm/IR/Module.h"
16#include "llvm/IR/Value.h"
17#include "llvm/Support/Debug.h"
18#include "llvm/Support/ModRef.h"
20#include <string>
21
22using namespace llvm;
23
24#define DEBUG_TYPE "mips16-hard-float"
25
26namespace {
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 {
39 }
40
41 bool runOnModule(Module &M) override;
42 };
43
44} // end anonymous namespace
45
46static 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(Type::getVoidTy(C), AsmArgTypes, false);
52 InlineAsm *IA = InlineAsm::get(AsmFTy, AsmText, "", true,
53 /* IsAlignStack */ false, InlineAsm::AD_ATT);
54 CallInst::Create(IA, AsmArgs, "", BB);
55}
56
57char Mips16HardFloat::ID = 0;
58
59//
60// Return types that matter for hard float are:
61// float, double, complex float, and complex double
62//
65};
66
67//
68// Determine which FP return type this function has
69//
71 switch (T->getTypeID()) {
72 case Type::FloatTyID:
73 return FRet;
75 return DRet;
76 case Type::StructTyID: {
77 StructType *ST = cast<StructType>(T);
78 if (ST->getNumElements() != 2)
79 break;
80 if ((ST->getElementType(0)->isFloatTy()) &&
81 (ST->getElementType(1)->isFloatTy()))
82 return CFRet;
83 if ((ST->getElementType(0)->isDoubleTy()) &&
84 (ST->getElementType(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)
99};
100
101// which floating point parameter signature variant we are dealing with
105
107 switch (F.arg_size()) {
108 case 0:
109 return NoSig;
110 case 1:{
111 TypeID ArgTypeID = F.getFunctionType()->getParamType(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(0)->getTypeID();
123 TypeID ArgTypeID1 = F.getFunctionType()->getParamType(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
157 if (F.arg_size() >=1) {
158 Type *ArgType = F.getFunctionType()->getParamType(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
171 Type* RetType = F.getReturnType();
172 return whichFPReturnVariant(RetType) != NoFPRet;
173}
174
176 Type* RetType = FT.getReturnType();
177 return whichFPReturnVariant(RetType) != NoFPRet;
178}
179
182}
183
184// We swap between FP and Integer registers to allow Mips16 and Mips32 to
185// interoperate
186static 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
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(StubName);
270 if (FStub && !FStub->isDeclaration()) return;
271 FStub = Function::Create(F.getFunctionType(),
272 Function::InternalLinkage, StubName, M);
273 FStub->addFnAttr("mips16_fp_stub");
274 FStub->addFnAttr(Attribute::Naked);
275 FStub->addFnAttr(Attribute::NoInline);
276 FStub->addFnAttr(Attribute::NoUnwind);
277 FStub->addFnAttr("nomips16");
278 FStub->setSection(SectionName);
279 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
282
283 std::string AsmText;
284 AsmText += ".set reorder\n";
285 AsmText += swapFPIntParams(PV, M, LE, 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(Context, BB, AsmText);
343
344 new UnreachableInst(Context, BB);
345}
346
347// Functions that are llvm intrinsics and don't need helpers.
348static 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
371 return llvm::binary_search(IntrinsicInline, F->getName());
372}
373
374// Returns of float, double and complex need to be handled with a helper
375// function.
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>(&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();
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];
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, "__Mips16RetHelper");
411 A = A.addFnAttribute(
413 A = A.addFnAttribute(C, Attribute::NoInline);
414 FunctionCallee F = (M->getOrInsertFunction(Name, A, MyVoid, T));
415 CallInst::Create(F, Params, "", I.getIterator());
416 } else if (const CallInst *CI = dyn_cast<CallInst>(&I)) {
417 FunctionType *FT = CI->getFunctionType();
418 Function *F_ = CI->getCalledFunction();
419 if (needsFPReturnHelper(*FT) &&
420 !(F_ && isIntrinsicInline(F_))) {
421 Modified=true;
422 F.addFnAttr("saveS2");
423 }
424 if (F_ && !isIntrinsicInline(F_)) {
425 // pic mode calls are handled by already defined
426 // helper functions
427 if (needsFPReturnHelper(*F_)) {
428 Modified=true;
429 F.addFnAttr("saveS2");
430 }
431 if (!TM.isPositionIndependent()) {
432 if (needsFPHelperFromSig(*F_)) {
433 assureFPCallStub(*F_, M, TM);
434 Modified=true;
435 }
436 }
437 }
438 }
439 }
440 return Modified;
441}
442
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;
453 (F->getFunctionType(),
454 Function::InternalLinkage, StubName, M);
455 FStub->addFnAttr("mips16_fp_stub");
456 FStub->addFnAttr(Attribute::Naked);
457 FStub->addFnAttr(Attribute::NoUnwind);
458 FStub->addFnAttr(Attribute::NoInline);
459 FStub->addFnAttr("nomips16");
460 FStub->setSection(SectionName);
461 BasicBlock *BB = BasicBlock::Create(Context, "entry", 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, false);
473 AsmText += "jr $$25\n";
474 AsmText += LocalName + " = " + Name + "\n";
475 emitInlineAsm(Context, BB, AsmText);
476
477 new UnreachableInst(FStub->getContext(), BB);
478}
479
480// remove the use-soft-float attribute
482 LLVM_DEBUG(errs() << "removing -use-soft-float\n");
483 F.removeFnAttr("use-soft-float");
484 if (F.hasFnAttribute("use-soft-float")) {
485 LLVM_DEBUG(errs() << "still has -use-soft-float\n");
486 }
487 F.addFnAttr("use-soft-float", "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.
506bool 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("nomips16") &&
513 F->hasFnAttribute("use-soft-float")) {
515 continue;
516 }
517 if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
518 F->hasFnAttribute("nomips16")) continue;
519 Modified |= fixupFPReturnAndCall(*F, &M, TM);
521 if (V != NoSig) {
522 Modified = true;
523 createFPFnStub(&*F, &M, V, TM);
524 }
525 }
526 return Modified;
527}
528
530 return new Mips16HardFloat();
531}
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
std::string Name
IRTranslator LLVM IR MI
Module.h This file contains the declarations for the Module class.
#define F(x, y, z)
Definition: MD5.cpp:55
#define I(x, y, z)
Definition: MD5.cpp:58
FPReturnVariant
@ CDRet
@ NoFPRet
@ DRet
@ CFRet
@ FRet
static bool fixupFPReturnAndCall(Function &F, Module *M, const MipsTargetMachine &TM)
static bool needsFPStubFromParams(Function &F)
static bool needsFPReturnHelper(Function &F)
static bool needsFPHelperFromSig(Function &F)
const Type::TypeID FloatTyID
static void createFPFnStub(Function *F, Module *M, FPParamVariant PV, const MipsTargetMachine &TM)
FPParamVariant
@ DDSig
@ DFSig
@ FDSig
@ NoSig
@ FFSig
@ DSig
@ FSig
static void removeUseSoftFloat(Function &F)
static const char *const IntrinsicInline[]
static bool isIntrinsicInline(Function *F)
static void emitInlineAsm(LLVMContext &C, BasicBlock *BB, StringRef AsmText)
static FPReturnVariant whichFPReturnVariant(Type *T)
const Type::TypeID DoubleTyID
static std::string swapFPIntParams(FPParamVariant PV, Module *M, bool LE, bool ToFP)
static FPParamVariant whichFPParamVariantNeeded(Function &F)
static void assureFPCallStub(Function &F, Module *M, const MipsTargetMachine &TM)
#define LLVM_DEBUG(...)
Definition: Debug.h:119
Target-Independent Code Generator Pass Configuration Options pass.
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
static LLVM_ABI Attribute getWithMemoryEffects(LLVMContext &Context, MemoryEffects ME)
Definition: Attributes.cpp:281
LLVM Basic Block Representation.
Definition: BasicBlock.h:62
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition: BasicBlock.h:206
This class represents a function call, abstracting a target machine's calling convention.
static CallInst * Create(FunctionType *Ty, Value *F, const Twine &NameStr="", InsertPosition InsertBefore=nullptr)
A handy container for a FunctionType+Callee-pointer pair, which can be passed around as a single enti...
Definition: DerivedTypes.h:170
void addFnAttr(Attribute::AttrKind Kind)
Add function attributes to this function.
Definition: Function.cpp:637
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
Definition: Function.h:166
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition: Function.cpp:359
Type * getReturnType() const
Returns the type of the ret val.
Definition: Function.h:214
LLVM_ABI void setSection(StringRef S)
Change the section for this global.
Definition: Globals.cpp:275
LLVM_ABI bool isDeclaration() const
Return true if the primary definition of this global value is outside of the current translation unit...
Definition: Globals.cpp:316
static LLVM_ABI InlineAsm * get(FunctionType *Ty, StringRef AsmString, StringRef Constraints, bool hasSideEffects, bool isAlignStack=false, AsmDialect asmDialect=AD_ATT, bool canThrow=false)
InlineAsm::get - Return the specified uniqued inline asm string.
Definition: InlineAsm.cpp:43
This is an important class for using LLVM in a threaded context.
Definition: LLVMContext.h:68
static MemoryEffectsBase none()
Create MemoryEffectsBase that cannot read or write any memory.
Definition: ModRef.h:120
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition: Pass.h:255
virtual bool runOnModule(Module &M)=0
runOnModule - Virtual method overriden by subclasses to process the module being operated on.
A Module instance is used to store all the information related to an LLVM module.
Definition: Module.h:67
FunctionListType::iterator iterator
The Function iterators.
Definition: Module.h:92
virtual void getAnalysisUsage(AnalysisUsage &) const
getAnalysisUsage - This function should be overriden by passes that need analysis information to do t...
Definition: Pass.cpp:112
virtual StringRef getPassName() const
getPassName - Return a nice clean name for a pass.
Definition: Pass.cpp:85
Return a value (possibly void), from a function.
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:55
Class to represent struct types.
Definition: DerivedTypes.h:218
Primary interface to the complete machine description for the target machine.
Definition: TargetMachine.h:83
Target-Independent Code Generator Pass Configuration Options.
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
TypeID
Definitions of all of the base types for the Type system.
Definition: Type.h:54
@ FloatTyID
32-bit floating point type
Definition: Type.h:58
@ StructTyID
Structures.
Definition: Type.h:73
@ DoubleTyID
64-bit floating point type
Definition: Type.h:59
static LLVM_ABI Type * getVoidTy(LLVMContext &C)
TypeID getTypeID() const
Return the type id for the type.
Definition: Type.h:136
This function has undefined behavior.
LLVM Value Representation.
Definition: Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition: Value.h:256
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition: CallingConv.h:24
@ C
The default llvm calling convention, compatible with C.
Definition: CallingConv.h:34
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
auto binary_search(R &&Range, T &&Value)
Provide wrappers to std::binary_search which take ranges instead of having to pass begin/end explicit...
Definition: STLExtras.h:2000
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
ModulePass * createMips16HardFloatPass()