Path: blob/main/contrib/llvm-project/llvm/lib/Target/Mips/Mips16HardFloat.cpp
35294 views
//===- Mips16HardFloat.cpp for Mips16 Hard Float --------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file defines a pass needed for Mips16 Hard Float9//10//===----------------------------------------------------------------------===//1112#include "MipsTargetMachine.h"13#include "llvm/CodeGen/TargetPassConfig.h"14#include "llvm/IR/Module.h"15#include "llvm/IR/Value.h"16#include "llvm/Support/Debug.h"17#include "llvm/Support/ModRef.h"18#include "llvm/Support/raw_ostream.h"19#include <algorithm>20#include <string>2122using namespace llvm;2324#define DEBUG_TYPE "mips16-hard-float"2526namespace {2728class Mips16HardFloat : public ModulePass {29public:30static char ID;3132Mips16HardFloat() : ModulePass(ID) {}3334StringRef getPassName() const override { return "MIPS16 Hard Float Pass"; }3536void getAnalysisUsage(AnalysisUsage &AU) const override {37AU.addRequired<TargetPassConfig>();38ModulePass::getAnalysisUsage(AU);39}4041bool runOnModule(Module &M) override;42};4344} // end anonymous namespace4546static void emitInlineAsm(LLVMContext &C, BasicBlock *BB, StringRef AsmText) {47std::vector<Type *> AsmArgTypes;48std::vector<Value *> AsmArgs;4950FunctionType *AsmFTy =51FunctionType::get(Type::getVoidTy(C), AsmArgTypes, false);52InlineAsm *IA = InlineAsm::get(AsmFTy, AsmText, "", true,53/* IsAlignStack */ false, InlineAsm::AD_ATT);54CallInst::Create(IA, AsmArgs, "", BB);55}5657char Mips16HardFloat::ID = 0;5859//60// Return types that matter for hard float are:61// float, double, complex float, and complex double62//63enum FPReturnVariant {64FRet, DRet, CFRet, CDRet, NoFPRet65};6667//68// Determine which FP return type this function has69//70static FPReturnVariant whichFPReturnVariant(Type *T) {71switch (T->getTypeID()) {72case Type::FloatTyID:73return FRet;74case Type::DoubleTyID:75return DRet;76case Type::StructTyID: {77StructType *ST = cast<StructType>(T);78if (ST->getNumElements() != 2)79break;80if ((ST->getElementType(0)->isFloatTy()) &&81(ST->getElementType(1)->isFloatTy()))82return CFRet;83if ((ST->getElementType(0)->isDoubleTy()) &&84(ST->getElementType(1)->isDoubleTy()))85return CDRet;86break;87}88default:89break;90}91return NoFPRet;92}9394// Parameter type that matter are float, (float, float), (float, double),95// double, (double, double), (double, float)96enum FPParamVariant {97FSig, FFSig, FDSig,98DSig, DDSig, DFSig, NoSig99};100101// which floating point parameter signature variant we are dealing with102using TypeID = Type::TypeID;103const Type::TypeID FloatTyID = Type::FloatTyID;104const Type::TypeID DoubleTyID = Type::DoubleTyID;105106static FPParamVariant whichFPParamVariantNeeded(Function &F) {107switch (F.arg_size()) {108case 0:109return NoSig;110case 1:{111TypeID ArgTypeID = F.getFunctionType()->getParamType(0)->getTypeID();112switch (ArgTypeID) {113case FloatTyID:114return FSig;115case DoubleTyID:116return DSig;117default:118return NoSig;119}120}121default: {122TypeID ArgTypeID0 = F.getFunctionType()->getParamType(0)->getTypeID();123TypeID ArgTypeID1 = F.getFunctionType()->getParamType(1)->getTypeID();124switch(ArgTypeID0) {125case FloatTyID: {126switch (ArgTypeID1) {127case FloatTyID:128return FFSig;129case DoubleTyID:130return FDSig;131default:132return FSig;133}134}135case DoubleTyID: {136switch (ArgTypeID1) {137case FloatTyID:138return DFSig;139case DoubleTyID:140return DDSig;141default:142return DSig;143}144}145default:146return NoSig;147}148}149}150llvm_unreachable("can't get here");151}152153// Figure out if we need float point based on the function parameters.154// We need to move variables in and/or out of floating point155// registers because of the ABI156static bool needsFPStubFromParams(Function &F) {157if (F.arg_size() >=1) {158Type *ArgType = F.getFunctionType()->getParamType(0);159switch (ArgType->getTypeID()) {160case Type::FloatTyID:161case Type::DoubleTyID:162return true;163default:164break;165}166}167return false;168}169170static bool needsFPReturnHelper(Function &F) {171Type* RetType = F.getReturnType();172return whichFPReturnVariant(RetType) != NoFPRet;173}174175static bool needsFPReturnHelper(FunctionType &FT) {176Type* RetType = FT.getReturnType();177return whichFPReturnVariant(RetType) != NoFPRet;178}179180static bool needsFPHelperFromSig(Function &F) {181return needsFPStubFromParams(F) || needsFPReturnHelper(F);182}183184// We swap between FP and Integer registers to allow Mips16 and Mips32 to185// interoperate186static std::string swapFPIntParams(FPParamVariant PV, Module *M, bool LE,187bool ToFP) {188std::string MI = ToFP ? "mtc1 ": "mfc1 ";189std::string AsmText;190191switch (PV) {192case FSig:193AsmText += MI + "$$4, $$f12\n";194break;195196case FFSig:197AsmText += MI + "$$4, $$f12\n";198AsmText += MI + "$$5, $$f14\n";199break;200201case FDSig:202AsmText += MI + "$$4, $$f12\n";203if (LE) {204AsmText += MI + "$$6, $$f14\n";205AsmText += MI + "$$7, $$f15\n";206} else {207AsmText += MI + "$$7, $$f14\n";208AsmText += MI + "$$6, $$f15\n";209}210break;211212case DSig:213if (LE) {214AsmText += MI + "$$4, $$f12\n";215AsmText += MI + "$$5, $$f13\n";216} else {217AsmText += MI + "$$5, $$f12\n";218AsmText += MI + "$$4, $$f13\n";219}220break;221222case DDSig:223if (LE) {224AsmText += MI + "$$4, $$f12\n";225AsmText += MI + "$$5, $$f13\n";226AsmText += MI + "$$6, $$f14\n";227AsmText += MI + "$$7, $$f15\n";228} else {229AsmText += MI + "$$5, $$f12\n";230AsmText += MI + "$$4, $$f13\n";231AsmText += MI + "$$7, $$f14\n";232AsmText += MI + "$$6, $$f15\n";233}234break;235236case DFSig:237if (LE) {238AsmText += MI + "$$4, $$f12\n";239AsmText += MI + "$$5, $$f13\n";240} else {241AsmText += MI + "$$5, $$f12\n";242AsmText += MI + "$$4, $$f13\n";243}244AsmText += MI + "$$6, $$f14\n";245break;246247case NoSig:248break;249}250251return AsmText;252}253254// Make sure that we know we already need a stub for this function.255// Having called needsFPHelperFromSig256static void assureFPCallStub(Function &F, Module *M,257const MipsTargetMachine &TM) {258// for now we only need them for static relocation259if (TM.isPositionIndependent())260return;261LLVMContext &Context = M->getContext();262bool LE = TM.isLittleEndian();263std::string Name(F.getName());264std::string SectionName = ".mips16.call.fp." + Name;265std::string StubName = "__call_stub_fp_" + Name;266//267// see if we already have the stub268//269Function *FStub = M->getFunction(StubName);270if (FStub && !FStub->isDeclaration()) return;271FStub = Function::Create(F.getFunctionType(),272Function::InternalLinkage, StubName, M);273FStub->addFnAttr("mips16_fp_stub");274FStub->addFnAttr(Attribute::Naked);275FStub->addFnAttr(Attribute::NoInline);276FStub->addFnAttr(Attribute::NoUnwind);277FStub->addFnAttr("nomips16");278FStub->setSection(SectionName);279BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);280FPReturnVariant RV = whichFPReturnVariant(FStub->getReturnType());281FPParamVariant PV = whichFPParamVariantNeeded(F);282283std::string AsmText;284AsmText += ".set reorder\n";285AsmText += swapFPIntParams(PV, M, LE, true);286if (RV != NoFPRet) {287AsmText += "move $$18, $$31\n";288AsmText += "jal " + Name + "\n";289} else {290AsmText += "lui $$25, %hi(" + Name + ")\n";291AsmText += "addiu $$25, $$25, %lo(" + Name + ")\n";292}293294switch (RV) {295case FRet:296AsmText += "mfc1 $$2, $$f0\n";297break;298299case DRet:300if (LE) {301AsmText += "mfc1 $$2, $$f0\n";302AsmText += "mfc1 $$3, $$f1\n";303} else {304AsmText += "mfc1 $$3, $$f0\n";305AsmText += "mfc1 $$2, $$f1\n";306}307break;308309case CFRet:310if (LE) {311AsmText += "mfc1 $$2, $$f0\n";312AsmText += "mfc1 $$3, $$f2\n";313} else {314AsmText += "mfc1 $$3, $$f0\n";315AsmText += "mfc1 $$3, $$f2\n";316}317break;318319case CDRet:320if (LE) {321AsmText += "mfc1 $$4, $$f2\n";322AsmText += "mfc1 $$5, $$f3\n";323AsmText += "mfc1 $$2, $$f0\n";324AsmText += "mfc1 $$3, $$f1\n";325326} else {327AsmText += "mfc1 $$5, $$f2\n";328AsmText += "mfc1 $$4, $$f3\n";329AsmText += "mfc1 $$3, $$f0\n";330AsmText += "mfc1 $$2, $$f1\n";331}332break;333334case NoFPRet:335break;336}337338if (RV != NoFPRet)339AsmText += "jr $$18\n";340else341AsmText += "jr $$25\n";342emitInlineAsm(Context, BB, AsmText);343344new UnreachableInst(Context, BB);345}346347// 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};369370static bool isIntrinsicInline(Function *F) {371return std::binary_search(std::begin(IntrinsicInline),372std::end(IntrinsicInline), F->getName());373}374375// Returns of float, double and complex need to be handled with a helper376// function.377static bool fixupFPReturnAndCall(Function &F, Module *M,378const MipsTargetMachine &TM) {379bool Modified = false;380LLVMContext &C = M->getContext();381Type *MyVoid = Type::getVoidTy(C);382for (auto &BB: F)383for (auto &I: BB) {384if (const ReturnInst *RI = dyn_cast<ReturnInst>(&I)) {385Value *RVal = RI->getReturnValue();386if (!RVal) continue;387//388// If there is a return value and it needs a helper function,389// figure out which one and add a call before the actual390// return to this helper. The purpose of the helper is to move391// floating point values from their soft float return mapping to392// where they would have been mapped to in floating point registers.393//394Type *T = RVal->getType();395FPReturnVariant RV = whichFPReturnVariant(T);396if (RV == NoFPRet) continue;397static const char *const Helper[NoFPRet] = {398"__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",399"__mips16_ret_dc"400};401const char *Name = Helper[RV];402AttributeList A;403Value *Params[] = {RVal};404Modified = true;405//406// These helper functions have a different calling ABI so407// this __Mips16RetHelper indicates that so that later408// during call setup, the proper call lowering to the helper409// functions will take place.410//411A = A.addFnAttribute(C, "__Mips16RetHelper");412A = A.addFnAttribute(413C, Attribute::getWithMemoryEffects(C, MemoryEffects::none()));414A = A.addFnAttribute(C, Attribute::NoInline);415FunctionCallee F = (M->getOrInsertFunction(Name, A, MyVoid, T));416CallInst::Create(F, Params, "", I.getIterator());417} else if (const CallInst *CI = dyn_cast<CallInst>(&I)) {418FunctionType *FT = CI->getFunctionType();419Function *F_ = CI->getCalledFunction();420if (needsFPReturnHelper(*FT) &&421!(F_ && isIntrinsicInline(F_))) {422Modified=true;423F.addFnAttr("saveS2");424}425if (F_ && !isIntrinsicInline(F_)) {426// pic mode calls are handled by already defined427// helper functions428if (needsFPReturnHelper(*F_)) {429Modified=true;430F.addFnAttr("saveS2");431}432if (!TM.isPositionIndependent()) {433if (needsFPHelperFromSig(*F_)) {434assureFPCallStub(*F_, M, TM);435Modified=true;436}437}438}439}440}441return Modified;442}443444static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,445const MipsTargetMachine &TM) {446bool PicMode = TM.isPositionIndependent();447bool LE = TM.isLittleEndian();448LLVMContext &Context = M->getContext();449std::string Name(F->getName());450std::string SectionName = ".mips16.fn." + Name;451std::string StubName = "__fn_stub_" + Name;452std::string LocalName = "$$__fn_local_" + Name;453Function *FStub = Function::Create454(F->getFunctionType(),455Function::InternalLinkage, StubName, M);456FStub->addFnAttr("mips16_fp_stub");457FStub->addFnAttr(Attribute::Naked);458FStub->addFnAttr(Attribute::NoUnwind);459FStub->addFnAttr(Attribute::NoInline);460FStub->addFnAttr("nomips16");461FStub->setSection(SectionName);462BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);463464std::string AsmText;465if (PicMode) {466AsmText += ".set noreorder\n";467AsmText += ".cpload $$25\n";468AsmText += ".set reorder\n";469AsmText += ".reloc 0, R_MIPS_NONE, " + Name + "\n";470AsmText += "la $$25, " + LocalName + "\n";471} else472AsmText += "la $$25, " + Name + "\n";473AsmText += swapFPIntParams(PV, M, LE, false);474AsmText += "jr $$25\n";475AsmText += LocalName + " = " + Name + "\n";476emitInlineAsm(Context, BB, AsmText);477478new UnreachableInst(FStub->getContext(), BB);479}480481// remove the use-soft-float attribute482static void removeUseSoftFloat(Function &F) {483LLVM_DEBUG(errs() << "removing -use-soft-float\n");484F.removeFnAttr("use-soft-float");485if (F.hasFnAttribute("use-soft-float")) {486LLVM_DEBUG(errs() << "still has -use-soft-float\n");487}488F.addFnAttr("use-soft-float", "false");489}490491// This pass only makes sense when the underlying chip has floating point but492// we are compiling as mips16.493// For all mips16 functions (that are not stubs we have already generated), or494// declared via attributes as nomips16, we must:495// 1) fixup all returns of float, double, single and double complex496// by calling a helper function before the actual return.497// 2) generate helper functions (stubs) that can be called by mips32498// functions that will move parameters passed normally passed in499// floating point500// registers the soft float equivalents.501// 3) in the case of static relocation, generate helper functions so that502// mips16 functions can call extern functions of unknown type (mips16 or503// mips32).504// 4) TBD. For pic, calls to extern functions of unknown type are handled by505// predefined helper functions in libc but this work is currently done506// during call lowering but it should be moved here in the future.507bool Mips16HardFloat::runOnModule(Module &M) {508auto &TM = static_cast<const MipsTargetMachine &>(509getAnalysis<TargetPassConfig>().getTM<TargetMachine>());510LLVM_DEBUG(errs() << "Run on Module Mips16HardFloat\n");511bool Modified = false;512for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) {513if (F->hasFnAttribute("nomips16") &&514F->hasFnAttribute("use-soft-float")) {515removeUseSoftFloat(*F);516continue;517}518if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||519F->hasFnAttribute("nomips16")) continue;520Modified |= fixupFPReturnAndCall(*F, &M, TM);521FPParamVariant V = whichFPParamVariantNeeded(*F);522if (V != NoSig) {523Modified = true;524createFPFnStub(&*F, &M, V, TM);525}526}527return Modified;528}529530ModulePass *llvm::createMips16HardFloatPass() {531return new Mips16HardFloat();532}533534535