Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/lib/Target/Mips/Mips16HardFloat.cpp
35294 views
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(Type::getVoidTy(C), AsmArgTypes, false);
53
InlineAsm *IA = InlineAsm::get(AsmFTy, AsmText, "", true,
54
/* IsAlignStack */ false, InlineAsm::AD_ATT);
55
CallInst::Create(IA, AsmArgs, "", 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>(T);
79
if (ST->getNumElements() != 2)
80
break;
81
if ((ST->getElementType(0)->isFloatTy()) &&
82
(ST->getElementType(1)->isFloatTy()))
83
return CFRet;
84
if ((ST->getElementType(0)->isDoubleTy()) &&
85
(ST->getElementType(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(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(0)->getTypeID();
124
TypeID ArgTypeID1 = F.getFunctionType()->getParamType(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(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(RetType) != NoFPRet;
174
}
175
176
static bool needsFPReturnHelper(FunctionType &FT) {
177
Type* RetType = FT.getReturnType();
178
return whichFPReturnVariant(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(StubName);
271
if (FStub && !FStub->isDeclaration()) return;
272
FStub = Function::Create(F.getFunctionType(),
273
Function::InternalLinkage, StubName, M);
274
FStub->addFnAttr("mips16_fp_stub");
275
FStub->addFnAttr(Attribute::Naked);
276
FStub->addFnAttr(Attribute::NoInline);
277
FStub->addFnAttr(Attribute::NoUnwind);
278
FStub->addFnAttr("nomips16");
279
FStub->setSection(SectionName);
280
BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
281
FPReturnVariant RV = whichFPReturnVariant(FStub->getReturnType());
282
FPParamVariant PV = whichFPParamVariantNeeded(F);
283
284
std::string AsmText;
285
AsmText += ".set reorder\n";
286
AsmText += swapFPIntParams(PV, M, LE, 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(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(std::begin(IntrinsicInline),
373
std::end(IntrinsicInline), 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>(&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, "__Mips16RetHelper");
413
A = A.addFnAttribute(
414
C, Attribute::getWithMemoryEffects(C, MemoryEffects::none()));
415
A = A.addFnAttribute(C, Attribute::NoInline);
416
FunctionCallee F = (M->getOrInsertFunction(Name, A, MyVoid, T));
417
CallInst::Create(F, Params, "", I.getIterator());
418
} else if (const CallInst *CI = dyn_cast<CallInst>(&I)) {
419
FunctionType *FT = CI->getFunctionType();
420
Function *F_ = CI->getCalledFunction();
421
if (needsFPReturnHelper(*FT) &&
422
!(F_ && isIntrinsicInline(F_))) {
423
Modified=true;
424
F.addFnAttr("saveS2");
425
}
426
if (F_ && !isIntrinsicInline(F_)) {
427
// pic mode calls are handled by already defined
428
// helper functions
429
if (needsFPReturnHelper(*F_)) {
430
Modified=true;
431
F.addFnAttr("saveS2");
432
}
433
if (!TM.isPositionIndependent()) {
434
if (needsFPHelperFromSig(*F_)) {
435
assureFPCallStub(*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
(F->getFunctionType(),
456
Function::InternalLinkage, StubName, M);
457
FStub->addFnAttr("mips16_fp_stub");
458
FStub->addFnAttr(Attribute::Naked);
459
FStub->addFnAttr(Attribute::NoUnwind);
460
FStub->addFnAttr(Attribute::NoInline);
461
FStub->addFnAttr("nomips16");
462
FStub->setSection(SectionName);
463
BasicBlock *BB = BasicBlock::Create(Context, "entry", 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, false);
475
AsmText += "jr $$25\n";
476
AsmText += LocalName + " = " + Name + "\n";
477
emitInlineAsm(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("use-soft-float");
486
if (F.hasFnAttribute("use-soft-float")) {
487
LLVM_DEBUG(errs() << "still has -use-soft-float\n");
488
}
489
F.addFnAttr("use-soft-float", "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("nomips16") &&
515
F->hasFnAttribute("use-soft-float")) {
516
removeUseSoftFloat(*F);
517
continue;
518
}
519
if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
520
F->hasFnAttribute("nomips16")) continue;
521
Modified |= fixupFPReturnAndCall(*F, &M, TM);
522
FPParamVariant V = whichFPParamVariantNeeded(*F);
523
if (V != NoSig) {
524
Modified = true;
525
createFPFnStub(&*F, &M, V, TM);
526
}
527
}
528
return Modified;
529
}
530
531
ModulePass *llvm::createMips16HardFloatPass() {
532
return new Mips16HardFloat();
533
}
534
535