2017-08-04 06:12:30 +08:00
|
|
|
//===- MipsAsmPrinter.cpp - Mips LLVM Assembly Printer --------------------===//
|
2007-06-06 15:42:06 +08:00
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
2007-12-30 04:36:04 +08:00
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
2007-06-06 15:42:06 +08:00
|
|
|
//
|
2011-04-16 05:51:11 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2007-06-06 15:42:06 +08:00
|
|
|
//
|
|
|
|
// This file contains a printer that converts from our internal representation
|
|
|
|
// of machine-dependent LLVM code to GAS-format MIPS assembly language.
|
|
|
|
//
|
2011-04-16 05:51:11 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2007-06-06 15:42:06 +08:00
|
|
|
|
2017-06-06 19:49:48 +08:00
|
|
|
#include "MipsAsmPrinter.h"
|
2011-07-08 07:56:50 +08:00
|
|
|
#include "InstPrinter/MipsInstPrinter.h"
|
2017-08-04 06:12:30 +08:00
|
|
|
#include "MCTargetDesc/MipsABIInfo.h"
|
2011-11-12 06:58:42 +08:00
|
|
|
#include "MCTargetDesc/MipsBaseInfo.h"
|
2014-02-28 18:00:38 +08:00
|
|
|
#include "MCTargetDesc/MipsMCNaCl.h"
|
2017-08-04 06:12:30 +08:00
|
|
|
#include "MCTargetDesc/MipsMCTargetDesc.h"
|
2012-12-04 00:50:05 +08:00
|
|
|
#include "Mips.h"
|
|
|
|
#include "MipsMCInstLower.h"
|
2017-08-04 06:12:30 +08:00
|
|
|
#include "MipsMachineFunction.h"
|
|
|
|
#include "MipsSubtarget.h"
|
2015-01-27 01:33:46 +08:00
|
|
|
#include "MipsTargetMachine.h"
|
2013-10-08 21:08:17 +08:00
|
|
|
#include "MipsTargetStreamer.h"
|
2011-11-09 06:26:47 +08:00
|
|
|
#include "llvm/ADT/SmallString.h"
|
2017-08-04 06:12:30 +08:00
|
|
|
#include "llvm/ADT/StringRef.h"
|
|
|
|
#include "llvm/ADT/Triple.h"
|
2011-11-09 06:26:47 +08:00
|
|
|
#include "llvm/ADT/Twine.h"
|
2017-06-07 11:48:56 +08:00
|
|
|
#include "llvm/BinaryFormat/ELF.h"
|
2017-08-04 06:12:30 +08:00
|
|
|
#include "llvm/CodeGen/MachineBasicBlock.h"
|
2007-06-06 15:42:06 +08:00
|
|
|
#include "llvm/CodeGen/MachineConstantPool.h"
|
2007-07-12 07:24:41 +08:00
|
|
|
#include "llvm/CodeGen/MachineFrameInfo.h"
|
2017-08-04 06:12:30 +08:00
|
|
|
#include "llvm/CodeGen/MachineFunction.h"
|
2007-06-06 15:42:06 +08:00
|
|
|
#include "llvm/CodeGen/MachineInstr.h"
|
2014-02-28 18:00:38 +08:00
|
|
|
#include "llvm/CodeGen/MachineJumpTableInfo.h"
|
2017-08-04 06:12:30 +08:00
|
|
|
#include "llvm/CodeGen/MachineOperand.h"
|
2017-11-17 09:07:10 +08:00
|
|
|
#include "llvm/CodeGen/TargetRegisterInfo.h"
|
|
|
|
#include "llvm/CodeGen/TargetSubtargetInfo.h"
|
2017-08-04 06:12:30 +08:00
|
|
|
#include "llvm/IR/Attributes.h"
|
2013-01-02 19:36:10 +08:00
|
|
|
#include "llvm/IR/BasicBlock.h"
|
|
|
|
#include "llvm/IR/DataLayout.h"
|
2017-08-04 06:12:30 +08:00
|
|
|
#include "llvm/IR/Function.h"
|
2013-01-02 19:36:10 +08:00
|
|
|
#include "llvm/IR/InlineAsm.h"
|
|
|
|
#include "llvm/IR/Instructions.h"
|
2014-01-27 09:33:33 +08:00
|
|
|
#include "llvm/MC/MCContext.h"
|
2014-02-15 03:16:39 +08:00
|
|
|
#include "llvm/MC/MCExpr.h"
|
2011-07-08 07:56:50 +08:00
|
|
|
#include "llvm/MC/MCInst.h"
|
2017-02-15 18:48:11 +08:00
|
|
|
#include "llvm/MC/MCInstBuilder.h"
|
2017-08-04 06:12:30 +08:00
|
|
|
#include "llvm/MC/MCObjectFileInfo.h"
|
2014-01-27 09:33:33 +08:00
|
|
|
#include "llvm/MC/MCSectionELF.h"
|
2017-08-04 06:12:30 +08:00
|
|
|
#include "llvm/MC/MCSymbol.h"
|
2015-06-02 08:25:12 +08:00
|
|
|
#include "llvm/MC/MCSymbolELF.h"
|
2017-08-04 06:12:30 +08:00
|
|
|
#include "llvm/Support/Casting.h"
|
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
2012-07-06 07:58:21 +08:00
|
|
|
#include "llvm/Support/TargetRegistry.h"
|
2012-12-04 00:50:05 +08:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2017-08-04 06:12:30 +08:00
|
|
|
#include "llvm/Target/TargetMachine.h"
|
|
|
|
#include <cassert>
|
|
|
|
#include <cstdint>
|
|
|
|
#include <map>
|
|
|
|
#include <memory>
|
2014-02-15 03:16:39 +08:00
|
|
|
#include <string>
|
2017-08-04 06:12:30 +08:00
|
|
|
#include <vector>
|
2011-07-01 09:04:43 +08:00
|
|
|
|
2007-06-06 15:42:06 +08:00
|
|
|
using namespace llvm;
|
|
|
|
|
2014-04-22 10:41:26 +08:00
|
|
|
#define DEBUG_TYPE "mips-asm-printer"
|
|
|
|
|
2014-12-17 18:56:16 +08:00
|
|
|
MipsTargetStreamer &MipsAsmPrinter::getTargetStreamer() const {
|
2015-04-25 03:11:51 +08:00
|
|
|
return static_cast<MipsTargetStreamer &>(*OutStreamer->getTargetStreamer());
|
2013-10-08 21:08:17 +08:00
|
|
|
}
|
|
|
|
|
2012-03-28 08:22:50 +08:00
|
|
|
bool MipsAsmPrinter::runOnMachineFunction(MachineFunction &MF) {
|
2015-02-20 16:39:06 +08:00
|
|
|
Subtarget = &MF.getSubtarget<MipsSubtarget>();
|
2014-07-18 08:08:53 +08:00
|
|
|
|
2012-03-28 08:22:50 +08:00
|
|
|
MipsFI = MF.getInfo<MipsFunctionInfo>();
|
2014-02-15 03:16:39 +08:00
|
|
|
if (Subtarget->inMips16Mode())
|
|
|
|
for (std::map<
|
|
|
|
const char *,
|
2017-08-04 06:12:30 +08:00
|
|
|
const Mips16HardFloatInfo::FuncSignature *>::const_iterator
|
2014-02-15 03:16:39 +08:00
|
|
|
it = MipsFI->StubsNeeded.begin();
|
|
|
|
it != MipsFI->StubsNeeded.end(); ++it) {
|
|
|
|
const char *Symbol = it->first;
|
2017-08-04 06:12:30 +08:00
|
|
|
const Mips16HardFloatInfo::FuncSignature *Signature = it->second;
|
2014-02-15 03:16:39 +08:00
|
|
|
if (StubsNeeded.find(Symbol) == StubsNeeded.end())
|
|
|
|
StubsNeeded[Symbol] = Signature;
|
|
|
|
}
|
2013-10-28 05:57:36 +08:00
|
|
|
MCP = MF.getConstantPool();
|
2014-02-28 18:00:38 +08:00
|
|
|
|
|
|
|
// In NaCl, all indirect jump targets must be aligned to bundle size.
|
|
|
|
if (Subtarget->isTargetNaCl())
|
|
|
|
NaClAlignIndirectJumpTargets(MF);
|
|
|
|
|
2012-03-28 08:22:50 +08:00
|
|
|
AsmPrinter::runOnMachineFunction(MF);
|
2017-02-15 18:48:11 +08:00
|
|
|
|
2017-05-04 19:03:50 +08:00
|
|
|
emitXRayTable();
|
2017-02-15 18:48:11 +08:00
|
|
|
|
2012-03-28 08:22:50 +08:00
|
|
|
return true;
|
2011-11-09 06:26:47 +08:00
|
|
|
}
|
|
|
|
|
2012-09-27 09:59:07 +08:00
|
|
|
bool MipsAsmPrinter::lowerOperand(const MachineOperand &MO, MCOperand &MCOp) {
|
|
|
|
MCOp = MCInstLowering.LowerOperand(MO);
|
|
|
|
return MCOp.isValid();
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "MipsGenMCPseudoLowering.inc"
|
|
|
|
|
2014-07-09 18:21:59 +08:00
|
|
|
// Lower PseudoReturn/PseudoIndirectBranch/PseudoIndirectBranch64 to JR, JR_MM,
|
2017-09-14 22:34:04 +08:00
|
|
|
// JALR, or JALR64 as appropriate for the target.
|
2014-07-09 18:21:59 +08:00
|
|
|
void MipsAsmPrinter::emitPseudoIndirectBranch(MCStreamer &OutStreamer,
|
|
|
|
const MachineInstr *MI) {
|
[mips][mips64r6] Use JALR for returns instead of JR (which is not available on MIPS32r6/MIPS64r6)
Summary:
RET, and RET_MM have been replaced by a pseudo named PseudoReturn.
In addition a version with a 64-bit GPR named PseudoReturn64 has been
added.
Instruction selection for a return matches RetRA, which is expanded post
register allocation to PseudoReturn/PseudoReturn64. During MipsAsmPrinter,
this PseudoReturn/PseudoReturn64 are emitted as:
- (JALR64 $zero, $rs) on MIPS64r6
- (JALR $zero, $rs) on MIPS32r6
- (JR_MM $rs) on microMIPS
- (JR $rs) otherwise
On MIPS32r6/MIPS64r6, 'jr $rs' is an alias for 'jalr $zero, $rs'. To aid
development and review (specifically, to ensure all cases of jr are
updated), these aliases are temporarily named 'r6.jr' instead of 'jr'.
A follow up patch will change them back to the correct mnemonic.
Added (JALR $zero, $rs) to MipsNaClELFStreamer's definition of an indirect
jump, and removed it from its definition of a call.
Note: I haven't accounted for MIPS64 in MipsNaClELFStreamer since it's
doesn't appear to account for any MIPS64-specifics.
The return instruction created as part of eh_return expansion is now expanded
using expandRetRA() so we use the right return instruction on MIPS32r6/MIPS64r6
('jalr $zero, $rs').
Also, fixed a misuse of isABI_N64() to detect 64-bit wide registers in
expandEhReturn().
Reviewers: jkolek, vmedic, mseaborn, zoran.jovanovic, dsanders
Reviewed By: dsanders
Subscribers: llvm-commits
Differential Revision: http://reviews.llvm.org/D4268
llvm-svn: 212604
2014-07-09 18:16:07 +08:00
|
|
|
bool HasLinkReg = false;
|
2016-08-18 21:22:43 +08:00
|
|
|
bool InMicroMipsMode = Subtarget->inMicroMipsMode();
|
[mips][mips64r6] Use JALR for returns instead of JR (which is not available on MIPS32r6/MIPS64r6)
Summary:
RET, and RET_MM have been replaced by a pseudo named PseudoReturn.
In addition a version with a 64-bit GPR named PseudoReturn64 has been
added.
Instruction selection for a return matches RetRA, which is expanded post
register allocation to PseudoReturn/PseudoReturn64. During MipsAsmPrinter,
this PseudoReturn/PseudoReturn64 are emitted as:
- (JALR64 $zero, $rs) on MIPS64r6
- (JALR $zero, $rs) on MIPS32r6
- (JR_MM $rs) on microMIPS
- (JR $rs) otherwise
On MIPS32r6/MIPS64r6, 'jr $rs' is an alias for 'jalr $zero, $rs'. To aid
development and review (specifically, to ensure all cases of jr are
updated), these aliases are temporarily named 'r6.jr' instead of 'jr'.
A follow up patch will change them back to the correct mnemonic.
Added (JALR $zero, $rs) to MipsNaClELFStreamer's definition of an indirect
jump, and removed it from its definition of a call.
Note: I haven't accounted for MIPS64 in MipsNaClELFStreamer since it's
doesn't appear to account for any MIPS64-specifics.
The return instruction created as part of eh_return expansion is now expanded
using expandRetRA() so we use the right return instruction on MIPS32r6/MIPS64r6
('jalr $zero, $rs').
Also, fixed a misuse of isABI_N64() to detect 64-bit wide registers in
expandEhReturn().
Reviewers: jkolek, vmedic, mseaborn, zoran.jovanovic, dsanders
Reviewed By: dsanders
Subscribers: llvm-commits
Differential Revision: http://reviews.llvm.org/D4268
llvm-svn: 212604
2014-07-09 18:16:07 +08:00
|
|
|
MCInst TmpInst0;
|
|
|
|
|
|
|
|
if (Subtarget->hasMips64r6()) {
|
|
|
|
// MIPS64r6 should use (JALR64 ZERO_64, $rs)
|
|
|
|
TmpInst0.setOpcode(Mips::JALR64);
|
|
|
|
HasLinkReg = true;
|
|
|
|
} else if (Subtarget->hasMips32r6()) {
|
|
|
|
// MIPS32r6 should use (JALR ZERO, $rs)
|
2016-08-18 21:22:43 +08:00
|
|
|
if (InMicroMipsMode)
|
|
|
|
TmpInst0.setOpcode(Mips::JRC16_MMR6);
|
|
|
|
else {
|
|
|
|
TmpInst0.setOpcode(Mips::JALR);
|
|
|
|
HasLinkReg = true;
|
|
|
|
}
|
[mips][mips64r6] Use JALR for returns instead of JR (which is not available on MIPS32r6/MIPS64r6)
Summary:
RET, and RET_MM have been replaced by a pseudo named PseudoReturn.
In addition a version with a 64-bit GPR named PseudoReturn64 has been
added.
Instruction selection for a return matches RetRA, which is expanded post
register allocation to PseudoReturn/PseudoReturn64. During MipsAsmPrinter,
this PseudoReturn/PseudoReturn64 are emitted as:
- (JALR64 $zero, $rs) on MIPS64r6
- (JALR $zero, $rs) on MIPS32r6
- (JR_MM $rs) on microMIPS
- (JR $rs) otherwise
On MIPS32r6/MIPS64r6, 'jr $rs' is an alias for 'jalr $zero, $rs'. To aid
development and review (specifically, to ensure all cases of jr are
updated), these aliases are temporarily named 'r6.jr' instead of 'jr'.
A follow up patch will change them back to the correct mnemonic.
Added (JALR $zero, $rs) to MipsNaClELFStreamer's definition of an indirect
jump, and removed it from its definition of a call.
Note: I haven't accounted for MIPS64 in MipsNaClELFStreamer since it's
doesn't appear to account for any MIPS64-specifics.
The return instruction created as part of eh_return expansion is now expanded
using expandRetRA() so we use the right return instruction on MIPS32r6/MIPS64r6
('jalr $zero, $rs').
Also, fixed a misuse of isABI_N64() to detect 64-bit wide registers in
expandEhReturn().
Reviewers: jkolek, vmedic, mseaborn, zoran.jovanovic, dsanders
Reviewed By: dsanders
Subscribers: llvm-commits
Differential Revision: http://reviews.llvm.org/D4268
llvm-svn: 212604
2014-07-09 18:16:07 +08:00
|
|
|
} else if (Subtarget->inMicroMipsMode())
|
|
|
|
// microMIPS should use (JR_MM $rs)
|
|
|
|
TmpInst0.setOpcode(Mips::JR_MM);
|
|
|
|
else {
|
|
|
|
// Everything else should use (JR $rs)
|
|
|
|
TmpInst0.setOpcode(Mips::JR);
|
|
|
|
}
|
|
|
|
|
|
|
|
MCOperand MCOp;
|
|
|
|
|
|
|
|
if (HasLinkReg) {
|
|
|
|
unsigned ZeroReg = Subtarget->isGP64bit() ? Mips::ZERO_64 : Mips::ZERO;
|
2015-05-14 02:37:00 +08:00
|
|
|
TmpInst0.addOperand(MCOperand::createReg(ZeroReg));
|
[mips][mips64r6] Use JALR for returns instead of JR (which is not available on MIPS32r6/MIPS64r6)
Summary:
RET, and RET_MM have been replaced by a pseudo named PseudoReturn.
In addition a version with a 64-bit GPR named PseudoReturn64 has been
added.
Instruction selection for a return matches RetRA, which is expanded post
register allocation to PseudoReturn/PseudoReturn64. During MipsAsmPrinter,
this PseudoReturn/PseudoReturn64 are emitted as:
- (JALR64 $zero, $rs) on MIPS64r6
- (JALR $zero, $rs) on MIPS32r6
- (JR_MM $rs) on microMIPS
- (JR $rs) otherwise
On MIPS32r6/MIPS64r6, 'jr $rs' is an alias for 'jalr $zero, $rs'. To aid
development and review (specifically, to ensure all cases of jr are
updated), these aliases are temporarily named 'r6.jr' instead of 'jr'.
A follow up patch will change them back to the correct mnemonic.
Added (JALR $zero, $rs) to MipsNaClELFStreamer's definition of an indirect
jump, and removed it from its definition of a call.
Note: I haven't accounted for MIPS64 in MipsNaClELFStreamer since it's
doesn't appear to account for any MIPS64-specifics.
The return instruction created as part of eh_return expansion is now expanded
using expandRetRA() so we use the right return instruction on MIPS32r6/MIPS64r6
('jalr $zero, $rs').
Also, fixed a misuse of isABI_N64() to detect 64-bit wide registers in
expandEhReturn().
Reviewers: jkolek, vmedic, mseaborn, zoran.jovanovic, dsanders
Reviewed By: dsanders
Subscribers: llvm-commits
Differential Revision: http://reviews.llvm.org/D4268
llvm-svn: 212604
2014-07-09 18:16:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
lowerOperand(MI->getOperand(0), MCOp);
|
|
|
|
TmpInst0.addOperand(MCOp);
|
|
|
|
|
|
|
|
EmitToStreamer(OutStreamer, TmpInst0);
|
|
|
|
}
|
|
|
|
|
2011-07-08 04:10:52 +08:00
|
|
|
void MipsAsmPrinter::EmitInstruction(const MachineInstr *MI) {
|
2014-07-08 16:59:22 +08:00
|
|
|
MipsTargetStreamer &TS = getTargetStreamer();
|
2017-02-15 18:48:11 +08:00
|
|
|
unsigned Opc = MI->getOpcode();
|
2014-08-14 17:18:14 +08:00
|
|
|
TS.forbidModuleDirective();
|
2014-07-08 18:11:38 +08:00
|
|
|
|
2011-07-08 04:10:52 +08:00
|
|
|
if (MI->isDebugValue()) {
|
2011-12-31 05:09:41 +08:00
|
|
|
SmallString<128> Str;
|
|
|
|
raw_svector_ostream OS(Str);
|
|
|
|
|
2011-07-08 04:10:52 +08:00
|
|
|
PrintDebugValueComment(MI, OS);
|
|
|
|
return;
|
|
|
|
}
|
2018-05-09 10:42:00 +08:00
|
|
|
if (MI->isDebugLabel())
|
|
|
|
return;
|
2007-06-06 15:42:06 +08:00
|
|
|
|
2013-10-28 05:57:36 +08:00
|
|
|
// If we just ended a constant pool, mark it as such.
|
2017-02-15 18:48:11 +08:00
|
|
|
if (InConstantPool && Opc != Mips::CONSTPOOL_ENTRY) {
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->EmitDataRegion(MCDR_DataRegionEnd);
|
2013-10-28 05:57:36 +08:00
|
|
|
InConstantPool = false;
|
|
|
|
}
|
2017-02-15 18:48:11 +08:00
|
|
|
if (Opc == Mips::CONSTPOOL_ENTRY) {
|
2013-10-28 05:57:36 +08:00
|
|
|
// CONSTPOOL_ENTRY - This instruction represents a floating
|
2017-02-15 18:48:11 +08:00
|
|
|
// constant pool in the function. The first operand is the ID#
|
2013-10-28 05:57:36 +08:00
|
|
|
// for this instruction, the second is the index into the
|
|
|
|
// MachineConstantPool that this is, the third is the size in
|
|
|
|
// bytes of this constant pool entry.
|
|
|
|
// The required alignment is specified on the basic block holding this MI.
|
|
|
|
//
|
|
|
|
unsigned LabelId = (unsigned)MI->getOperand(0).getImm();
|
2017-02-15 18:48:11 +08:00
|
|
|
unsigned CPIdx = (unsigned)MI->getOperand(1).getIndex();
|
2013-10-28 05:57:36 +08:00
|
|
|
|
|
|
|
// If this is the first entry of the pool, mark it.
|
|
|
|
if (!InConstantPool) {
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->EmitDataRegion(MCDR_DataRegion);
|
2013-10-28 05:57:36 +08:00
|
|
|
InConstantPool = true;
|
|
|
|
}
|
|
|
|
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->EmitLabel(GetCPISymbol(LabelId));
|
2013-10-28 05:57:36 +08:00
|
|
|
|
|
|
|
const MachineConstantPoolEntry &MCPE = MCP->getConstants()[CPIdx];
|
|
|
|
if (MCPE.isMachineConstantPoolEntry())
|
|
|
|
EmitMachineConstantPoolValue(MCPE.Val.MachineCPVal);
|
|
|
|
else
|
2015-07-16 14:11:10 +08:00
|
|
|
EmitGlobalConstant(MF->getDataLayout(), MCPE.Val.ConstVal);
|
2013-10-28 05:57:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-15 18:48:11 +08:00
|
|
|
switch (Opc) {
|
|
|
|
case Mips::PATCHABLE_FUNCTION_ENTER:
|
|
|
|
LowerPATCHABLE_FUNCTION_ENTER(*MI);
|
|
|
|
return;
|
|
|
|
case Mips::PATCHABLE_FUNCTION_EXIT:
|
|
|
|
LowerPATCHABLE_FUNCTION_EXIT(*MI);
|
|
|
|
return;
|
|
|
|
case Mips::PATCHABLE_TAIL_CALL:
|
|
|
|
LowerPATCHABLE_TAIL_CALL(*MI);
|
|
|
|
return;
|
|
|
|
}
|
2016-02-23 04:49:58 +08:00
|
|
|
|
|
|
|
MachineBasicBlock::const_instr_iterator I = MI->getIterator();
|
|
|
|
MachineBasicBlock::const_instr_iterator E = MI->getParent()->instr_end();
|
2012-06-14 07:25:52 +08:00
|
|
|
|
|
|
|
do {
|
2013-02-07 05:50:15 +08:00
|
|
|
// Do any auto-generated pseudo lowerings.
|
2015-04-25 03:11:51 +08:00
|
|
|
if (emitPseudoExpansionLowering(*OutStreamer, &*I))
|
2013-02-07 05:50:15 +08:00
|
|
|
continue;
|
2012-08-29 03:07:39 +08:00
|
|
|
|
[mips][mips64r6] Use JALR for returns instead of JR (which is not available on MIPS32r6/MIPS64r6)
Summary:
RET, and RET_MM have been replaced by a pseudo named PseudoReturn.
In addition a version with a 64-bit GPR named PseudoReturn64 has been
added.
Instruction selection for a return matches RetRA, which is expanded post
register allocation to PseudoReturn/PseudoReturn64. During MipsAsmPrinter,
this PseudoReturn/PseudoReturn64 are emitted as:
- (JALR64 $zero, $rs) on MIPS64r6
- (JALR $zero, $rs) on MIPS32r6
- (JR_MM $rs) on microMIPS
- (JR $rs) otherwise
On MIPS32r6/MIPS64r6, 'jr $rs' is an alias for 'jalr $zero, $rs'. To aid
development and review (specifically, to ensure all cases of jr are
updated), these aliases are temporarily named 'r6.jr' instead of 'jr'.
A follow up patch will change them back to the correct mnemonic.
Added (JALR $zero, $rs) to MipsNaClELFStreamer's definition of an indirect
jump, and removed it from its definition of a call.
Note: I haven't accounted for MIPS64 in MipsNaClELFStreamer since it's
doesn't appear to account for any MIPS64-specifics.
The return instruction created as part of eh_return expansion is now expanded
using expandRetRA() so we use the right return instruction on MIPS32r6/MIPS64r6
('jalr $zero, $rs').
Also, fixed a misuse of isABI_N64() to detect 64-bit wide registers in
expandEhReturn().
Reviewers: jkolek, vmedic, mseaborn, zoran.jovanovic, dsanders
Reviewed By: dsanders
Subscribers: llvm-commits
Differential Revision: http://reviews.llvm.org/D4268
llvm-svn: 212604
2014-07-09 18:16:07 +08:00
|
|
|
if (I->getOpcode() == Mips::PseudoReturn ||
|
2014-07-09 18:21:59 +08:00
|
|
|
I->getOpcode() == Mips::PseudoReturn64 ||
|
|
|
|
I->getOpcode() == Mips::PseudoIndirectBranch ||
|
2016-08-18 21:22:43 +08:00
|
|
|
I->getOpcode() == Mips::PseudoIndirectBranch64 ||
|
|
|
|
I->getOpcode() == Mips::TAILCALLREG ||
|
|
|
|
I->getOpcode() == Mips::TAILCALLREG64) {
|
2015-04-25 03:11:51 +08:00
|
|
|
emitPseudoIndirectBranch(*OutStreamer, &*I);
|
[mips][mips64r6] Use JALR for returns instead of JR (which is not available on MIPS32r6/MIPS64r6)
Summary:
RET, and RET_MM have been replaced by a pseudo named PseudoReturn.
In addition a version with a 64-bit GPR named PseudoReturn64 has been
added.
Instruction selection for a return matches RetRA, which is expanded post
register allocation to PseudoReturn/PseudoReturn64. During MipsAsmPrinter,
this PseudoReturn/PseudoReturn64 are emitted as:
- (JALR64 $zero, $rs) on MIPS64r6
- (JALR $zero, $rs) on MIPS32r6
- (JR_MM $rs) on microMIPS
- (JR $rs) otherwise
On MIPS32r6/MIPS64r6, 'jr $rs' is an alias for 'jalr $zero, $rs'. To aid
development and review (specifically, to ensure all cases of jr are
updated), these aliases are temporarily named 'r6.jr' instead of 'jr'.
A follow up patch will change them back to the correct mnemonic.
Added (JALR $zero, $rs) to MipsNaClELFStreamer's definition of an indirect
jump, and removed it from its definition of a call.
Note: I haven't accounted for MIPS64 in MipsNaClELFStreamer since it's
doesn't appear to account for any MIPS64-specifics.
The return instruction created as part of eh_return expansion is now expanded
using expandRetRA() so we use the right return instruction on MIPS32r6/MIPS64r6
('jalr $zero, $rs').
Also, fixed a misuse of isABI_N64() to detect 64-bit wide registers in
expandEhReturn().
Reviewers: jkolek, vmedic, mseaborn, zoran.jovanovic, dsanders
Reviewed By: dsanders
Subscribers: llvm-commits
Differential Revision: http://reviews.llvm.org/D4268
llvm-svn: 212604
2014-07-09 18:16:07 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-02-16 05:05:58 +08:00
|
|
|
// The inMips16Mode() test is not permanent.
|
|
|
|
// Some instructions are marked as pseudo right now which
|
|
|
|
// would make the test fail for the wrong reason but
|
|
|
|
// that will be fixed soon. We need this here because we are
|
|
|
|
// removing another test for this situation downstream in the
|
|
|
|
// callchain.
|
|
|
|
//
|
2014-04-30 23:06:25 +08:00
|
|
|
if (I->isPseudo() && !Subtarget->inMips16Mode()
|
|
|
|
&& !isLongBranchPseudo(I->getOpcode()))
|
2013-02-16 05:05:58 +08:00
|
|
|
llvm_unreachable("Pseudo opcode found in EmitInstruction()");
|
|
|
|
|
2013-02-07 05:50:15 +08:00
|
|
|
MCInst TmpInst0;
|
2015-10-20 08:15:20 +08:00
|
|
|
MCInstLowering.Lower(&*I, TmpInst0);
|
2015-04-25 03:11:51 +08:00
|
|
|
EmitToStreamer(*OutStreamer, TmpInst0);
|
2013-02-07 05:50:15 +08:00
|
|
|
} while ((++I != E) && I->isInsideBundle()); // Delay slot check
|
2011-07-08 04:10:52 +08:00
|
|
|
}
|
2007-06-06 15:42:06 +08:00
|
|
|
|
2011-04-16 05:51:11 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2007-08-28 13:06:17 +08:00
|
|
|
//
|
|
|
|
// Mips Asm Directives
|
|
|
|
//
|
|
|
|
// -- Frame directive "frame Stackpointer, Stacksize, RARegister"
|
|
|
|
// Describe the stack frame.
|
|
|
|
//
|
2011-03-05 01:51:39 +08:00
|
|
|
// -- Mask directives "(f)mask bitmask, offset"
|
2007-08-28 13:06:17 +08:00
|
|
|
// Tells the assembler which registers are saved and where.
|
2011-03-05 01:51:39 +08:00
|
|
|
// bitmask - contain a little endian bitset indicating which registers are
|
|
|
|
// saved on function prologue (e.g. with a 0x80000000 mask, the
|
2007-08-28 13:06:17 +08:00
|
|
|
// assembler knows the register 31 (RA) is saved at prologue.
|
2011-03-05 01:51:39 +08:00
|
|
|
// offset - the position before stack pointer subtraction indicating where
|
2007-08-28 13:06:17 +08:00
|
|
|
// the first saved register on prologue is located. (e.g. with a
|
|
|
|
//
|
|
|
|
// Consider the following function prologue:
|
|
|
|
//
|
2008-02-27 14:33:05 +08:00
|
|
|
// .frame $fp,48,$ra
|
|
|
|
// .mask 0xc0000000,-8
|
|
|
|
// addiu $sp, $sp, -48
|
|
|
|
// sw $ra, 40($sp)
|
|
|
|
// sw $fp, 36($sp)
|
2007-08-28 13:06:17 +08:00
|
|
|
//
|
2011-03-05 01:51:39 +08:00
|
|
|
// With a 0xc0000000 mask, the assembler knows the register 31 (RA) and
|
|
|
|
// 30 (FP) are saved at prologue. As the save order on prologue is from
|
|
|
|
// left to right, RA is saved first. A -8 offset means that after the
|
2007-08-28 13:06:17 +08:00
|
|
|
// stack pointer subtration, the first register in the mask (RA) will be
|
|
|
|
// saved at address 48-8=40.
|
|
|
|
//
|
2011-04-16 05:51:11 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2007-07-12 07:24:41 +08:00
|
|
|
|
2011-04-16 05:51:11 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2008-07-14 22:42:54 +08:00
|
|
|
// Mask directives
|
2011-04-16 05:51:11 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2008-07-14 22:42:54 +08:00
|
|
|
|
2011-03-05 01:51:39 +08:00
|
|
|
// Create a bitmask with all callee saved registers for CPU or Floating Point
|
2008-08-06 14:14:43 +08:00
|
|
|
// registers. For CPU registers consider RA, GP and FP for saving if necessary.
|
2014-01-27 12:33:11 +08:00
|
|
|
void MipsAsmPrinter::printSavedRegsBitmask() {
|
2008-08-06 14:14:43 +08:00
|
|
|
// CPU and FPU Saved Registers Bitmasks
|
2011-05-24 04:34:30 +08:00
|
|
|
unsigned CPUBitmask = 0, FPUBitmask = 0;
|
|
|
|
int CPUTopSavedRegOff, FPUTopSavedRegOff;
|
2007-08-28 13:06:17 +08:00
|
|
|
|
2008-08-06 14:14:43 +08:00
|
|
|
// Set the CPU and FPU Bitmasks
|
2016-07-29 02:40:00 +08:00
|
|
|
const MachineFrameInfo &MFI = MF->getFrameInfo();
|
2015-03-21 11:13:07 +08:00
|
|
|
const TargetRegisterInfo *TRI = MF->getSubtarget().getRegisterInfo();
|
2016-07-29 02:40:00 +08:00
|
|
|
const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo();
|
2011-05-24 04:34:30 +08:00
|
|
|
// size of stack area to which FP callee-saved regs are saved.
|
2017-04-25 02:55:33 +08:00
|
|
|
unsigned CPURegSize = TRI->getRegSizeInBits(Mips::GPR32RegClass) / 8;
|
|
|
|
unsigned FGR32RegSize = TRI->getRegSizeInBits(Mips::FGR32RegClass) / 8;
|
|
|
|
unsigned AFGR64RegSize = TRI->getRegSizeInBits(Mips::AFGR64RegClass) / 8;
|
2011-05-24 04:34:30 +08:00
|
|
|
bool HasAFGR64Reg = false;
|
|
|
|
unsigned CSFPRegsSize = 0;
|
|
|
|
|
2015-04-09 18:54:16 +08:00
|
|
|
for (const auto &I : CSI) {
|
|
|
|
unsigned Reg = I.getReg();
|
2015-03-21 11:13:07 +08:00
|
|
|
unsigned RegNum = TRI->getEncodingValue(Reg);
|
2015-04-09 18:54:16 +08:00
|
|
|
|
|
|
|
// If it's a floating point register, set the FPU Bitmask.
|
|
|
|
// If it's a general purpose register, set the CPU Bitmask.
|
|
|
|
if (Mips::FGR32RegClass.contains(Reg)) {
|
|
|
|
FPUBitmask |= (1 << RegNum);
|
|
|
|
CSFPRegsSize += FGR32RegSize;
|
|
|
|
} else if (Mips::AFGR64RegClass.contains(Reg)) {
|
2011-05-24 04:34:30 +08:00
|
|
|
FPUBitmask |= (3 << RegNum);
|
|
|
|
CSFPRegsSize += AFGR64RegSize;
|
|
|
|
HasAFGR64Reg = true;
|
2015-04-09 18:54:16 +08:00
|
|
|
} else if (Mips::GPR32RegClass.contains(Reg))
|
|
|
|
CPUBitmask |= (1 << RegNum);
|
2008-08-06 14:14:43 +08:00
|
|
|
}
|
2007-08-28 13:06:17 +08:00
|
|
|
|
2011-05-24 04:34:30 +08:00
|
|
|
// FP Regs are saved right below where the virtual frame pointer points to.
|
|
|
|
FPUTopSavedRegOff = FPUBitmask ?
|
|
|
|
(HasAFGR64Reg ? -AFGR64RegSize : -FGR32RegSize) : 0;
|
2010-11-19 05:19:35 +08:00
|
|
|
|
2011-05-24 04:34:30 +08:00
|
|
|
// CPU Regs are saved below FP Regs.
|
|
|
|
CPUTopSavedRegOff = CPUBitmask ? -CSFPRegsSize - CPURegSize : 0;
|
2007-08-28 13:06:17 +08:00
|
|
|
|
2014-01-27 12:33:11 +08:00
|
|
|
MipsTargetStreamer &TS = getTargetStreamer();
|
2008-08-06 14:14:43 +08:00
|
|
|
// Print CPUBitmask
|
2014-01-27 12:33:11 +08:00
|
|
|
TS.emitMask(CPUBitmask, CPUTopSavedRegOff);
|
2008-08-06 14:14:43 +08:00
|
|
|
|
|
|
|
// Print FPUBitmask
|
2014-01-27 12:33:11 +08:00
|
|
|
TS.emitFMask(FPUBitmask, FPUTopSavedRegOff);
|
2007-07-12 07:24:41 +08:00
|
|
|
}
|
|
|
|
|
2011-04-16 05:51:11 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2008-07-14 22:42:54 +08:00
|
|
|
// Frame and Set directives
|
2011-04-16 05:51:11 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2008-07-14 22:42:54 +08:00
|
|
|
|
|
|
|
/// Frame Directive
|
2010-04-04 15:05:53 +08:00
|
|
|
void MipsAsmPrinter::emitFrameDirective() {
|
2015-03-21 11:13:07 +08:00
|
|
|
const TargetRegisterInfo &RI = *MF->getSubtarget().getRegisterInfo();
|
2008-07-14 22:42:54 +08:00
|
|
|
|
2010-01-28 14:22:43 +08:00
|
|
|
unsigned stackReg = RI.getFrameRegister(*MF);
|
2008-07-14 22:42:54 +08:00
|
|
|
unsigned returnReg = RI.getRARegister();
|
2016-07-29 02:40:00 +08:00
|
|
|
unsigned stackSize = MF->getFrameInfo().getStackSize();
|
2008-07-14 22:42:54 +08:00
|
|
|
|
2014-01-27 11:53:56 +08:00
|
|
|
getTargetStreamer().emitFrame(stackReg, stackSize, returnReg);
|
2008-07-14 22:42:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Emit Set directives.
|
2011-03-05 01:51:39 +08:00
|
|
|
const char *MipsAsmPrinter::getCurrentABIString() const {
|
2015-01-27 01:33:46 +08:00
|
|
|
switch (static_cast<MipsTargetMachine &>(TM).getABI().GetEnumValue()) {
|
2014-10-25 00:15:27 +08:00
|
|
|
case MipsABIInfo::ABI::O32: return "abi32";
|
|
|
|
case MipsABIInfo::ABI::N32: return "abiN32";
|
|
|
|
case MipsABIInfo::ABI::N64: return "abi64";
|
2012-09-11 05:26:47 +08:00
|
|
|
default: llvm_unreachable("Unknown Mips ABI");
|
2008-07-14 22:42:54 +08:00
|
|
|
}
|
2011-03-05 01:51:39 +08:00
|
|
|
}
|
2008-07-14 22:42:54 +08:00
|
|
|
|
2010-01-28 07:23:58 +08:00
|
|
|
void MipsAsmPrinter::EmitFunctionEntryLabel() {
|
2014-01-15 02:57:12 +08:00
|
|
|
MipsTargetStreamer &TS = getTargetStreamer();
|
2014-02-28 18:00:38 +08:00
|
|
|
|
|
|
|
// NaCl sandboxing requires that indirect call instructions are masked.
|
|
|
|
// This means that function entry points should be bundle-aligned.
|
|
|
|
if (Subtarget->isTargetNaCl())
|
|
|
|
EmitAlignment(std::max(MF->getAlignment(), MIPS_NACL_BUNDLE_ALIGN));
|
|
|
|
|
2016-06-16 17:17:03 +08:00
|
|
|
if (Subtarget->inMicroMipsMode()) {
|
2014-01-15 02:57:12 +08:00
|
|
|
TS.emitDirectiveSetMicroMips();
|
2016-06-16 17:17:03 +08:00
|
|
|
TS.setUsesMicroMips();
|
2017-11-24 22:00:47 +08:00
|
|
|
TS.updateABIInfo(*Subtarget);
|
2016-06-16 17:17:03 +08:00
|
|
|
} else
|
2014-04-16 19:46:59 +08:00
|
|
|
TS.emitDirectiveSetNoMicroMips();
|
2014-01-14 12:25:13 +08:00
|
|
|
|
2014-01-15 02:57:12 +08:00
|
|
|
if (Subtarget->inMips16Mode())
|
|
|
|
TS.emitDirectiveSetMips16();
|
|
|
|
else
|
|
|
|
TS.emitDirectiveSetNoMips16();
|
2013-02-20 06:04:37 +08:00
|
|
|
|
2014-01-15 02:57:12 +08:00
|
|
|
TS.emitDirectiveEnt(*CurrentFnSym);
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->EmitLabel(CurrentFnSym);
|
2010-01-28 07:23:58 +08:00
|
|
|
}
|
|
|
|
|
2010-01-28 14:22:43 +08:00
|
|
|
/// EmitFunctionBodyStart - Targets can override this to emit stuff before
|
|
|
|
/// the first basic block in the function.
|
|
|
|
void MipsAsmPrinter::EmitFunctionBodyStart() {
|
2014-01-26 13:06:48 +08:00
|
|
|
MipsTargetStreamer &TS = getTargetStreamer();
|
|
|
|
|
2013-10-30 00:24:21 +08:00
|
|
|
MCInstLowering.Initialize(&MF->getContext());
|
2012-03-28 08:22:50 +08:00
|
|
|
|
2017-12-16 06:22:58 +08:00
|
|
|
bool IsNakedFunction = MF->getFunction().hasFnAttribute(Attribute::Naked);
|
2013-05-04 07:17:24 +08:00
|
|
|
if (!IsNakedFunction)
|
|
|
|
emitFrameDirective();
|
2011-03-05 01:51:39 +08:00
|
|
|
|
2014-01-27 12:33:11 +08:00
|
|
|
if (!IsNakedFunction)
|
|
|
|
printSavedRegsBitmask();
|
|
|
|
|
2014-01-26 13:06:48 +08:00
|
|
|
if (!Subtarget->inMips16Mode()) {
|
|
|
|
TS.emitDirectiveSetNoReorder();
|
|
|
|
TS.emitDirectiveSetNoMacro();
|
|
|
|
TS.emitDirectiveSetNoAt();
|
2012-05-12 08:48:43 +08:00
|
|
|
}
|
2010-01-28 14:22:43 +08:00
|
|
|
}
|
2007-06-06 15:42:06 +08:00
|
|
|
|
2010-01-28 14:22:43 +08:00
|
|
|
/// EmitFunctionBodyEnd - Targets can override this to emit stuff after
|
|
|
|
/// the last basic block in the function.
|
|
|
|
void MipsAsmPrinter::EmitFunctionBodyEnd() {
|
2014-01-26 13:06:48 +08:00
|
|
|
MipsTargetStreamer &TS = getTargetStreamer();
|
|
|
|
|
2010-01-28 09:48:52 +08:00
|
|
|
// There are instruction for this macros, but they must
|
|
|
|
// always be at the function end, and we can't emit and
|
2011-03-05 01:51:39 +08:00
|
|
|
// break with BB logic.
|
2014-01-26 13:06:48 +08:00
|
|
|
if (!Subtarget->inMips16Mode()) {
|
|
|
|
TS.emitDirectiveSetAt();
|
|
|
|
TS.emitDirectiveSetMacro();
|
|
|
|
TS.emitDirectiveSetReorder();
|
2011-11-09 06:26:47 +08:00
|
|
|
}
|
2014-01-26 13:06:48 +08:00
|
|
|
TS.emitDirectiveEnd(CurrentFnSym->getName());
|
2013-10-28 05:57:36 +08:00
|
|
|
// Make sure to terminate any constant pools that were at the end
|
|
|
|
// of the function.
|
|
|
|
if (!InConstantPool)
|
|
|
|
return;
|
|
|
|
InConstantPool = false;
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->EmitDataRegion(MCDR_DataRegionEnd);
|
2007-06-06 15:42:06 +08:00
|
|
|
}
|
|
|
|
|
2015-05-08 17:10:15 +08:00
|
|
|
void MipsAsmPrinter::EmitBasicBlockEnd(const MachineBasicBlock &MBB) {
|
2017-10-24 14:16:03 +08:00
|
|
|
AsmPrinter::EmitBasicBlockEnd(MBB);
|
2015-05-08 17:10:15 +08:00
|
|
|
MipsTargetStreamer &TS = getTargetStreamer();
|
2017-08-04 06:12:30 +08:00
|
|
|
if (MBB.empty())
|
2015-05-08 17:10:15 +08:00
|
|
|
TS.emitDirectiveInsn();
|
|
|
|
}
|
|
|
|
|
2010-07-20 16:37:04 +08:00
|
|
|
/// isBlockOnlyReachableByFallthough - Return true if the basic block has
|
|
|
|
/// exactly one predecessor and the control transfer mechanism between
|
|
|
|
/// the predecessor and this block is a fall-through.
|
2011-04-16 05:51:11 +08:00
|
|
|
bool MipsAsmPrinter::isBlockOnlyReachableByFallthrough(const MachineBasicBlock*
|
|
|
|
MBB) const {
|
2010-07-20 16:37:04 +08:00
|
|
|
// The predecessor has to be immediately before this block.
|
|
|
|
const MachineBasicBlock *Pred = *MBB->pred_begin();
|
|
|
|
|
|
|
|
// If the predecessor is a switch statement, assume a jump table
|
|
|
|
// implementation, so it is not a fall through.
|
|
|
|
if (const BasicBlock *bb = Pred->getBasicBlock())
|
|
|
|
if (isa<SwitchInst>(bb->getTerminator()))
|
|
|
|
return false;
|
2011-03-05 01:51:39 +08:00
|
|
|
|
2011-04-02 02:57:38 +08:00
|
|
|
// If this is a landing pad, it isn't a fall through. If it has no preds,
|
|
|
|
// then nothing falls through to it.
|
2015-08-28 07:27:47 +08:00
|
|
|
if (MBB->isEHPad() || MBB->pred_empty())
|
2011-04-02 02:57:38 +08:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// If there isn't exactly one predecessor, it can't be a fall through.
|
|
|
|
MachineBasicBlock::const_pred_iterator PI = MBB->pred_begin(), PI2 = PI;
|
|
|
|
++PI2;
|
2012-02-28 15:46:26 +08:00
|
|
|
|
2011-04-02 02:57:38 +08:00
|
|
|
if (PI2 != MBB->pred_end())
|
2012-02-28 15:46:26 +08:00
|
|
|
return false;
|
2011-04-02 02:57:38 +08:00
|
|
|
|
|
|
|
// The predecessor has to be immediately before this block.
|
|
|
|
if (!Pred->isLayoutSuccessor(MBB))
|
|
|
|
return false;
|
2012-02-28 15:46:26 +08:00
|
|
|
|
2011-04-02 02:57:38 +08:00
|
|
|
// If the block is completely empty, then it definitely does fall through.
|
|
|
|
if (Pred->empty())
|
|
|
|
return true;
|
2012-02-28 15:46:26 +08:00
|
|
|
|
2011-04-02 02:57:38 +08:00
|
|
|
// Otherwise, check the last instruction.
|
|
|
|
// Check if the last terminator is an unconditional branch.
|
|
|
|
MachineBasicBlock::const_iterator I = Pred->end();
|
2011-12-07 15:15:52 +08:00
|
|
|
while (I != Pred->begin() && !(--I)->isTerminator()) ;
|
2011-04-02 02:57:38 +08:00
|
|
|
|
2011-12-07 15:15:52 +08:00
|
|
|
return !I->isBarrier();
|
2010-07-20 16:37:04 +08:00
|
|
|
}
|
|
|
|
|
2008-08-03 03:42:36 +08:00
|
|
|
// Print out an operand for an inline asm expression.
|
2012-05-11 05:48:22 +08:00
|
|
|
bool MipsAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
|
2015-03-24 19:26:34 +08:00
|
|
|
unsigned AsmVariant, const char *ExtraCode,
|
2010-04-04 13:29:35 +08:00
|
|
|
raw_ostream &O) {
|
2008-08-03 03:42:36 +08:00
|
|
|
// Does this asm operand have a single letter operand modifier?
|
2012-05-11 05:48:22 +08:00
|
|
|
if (ExtraCode && ExtraCode[0]) {
|
|
|
|
if (ExtraCode[1] != 0) return true; // Unknown modifier.
|
|
|
|
|
|
|
|
const MachineOperand &MO = MI->getOperand(OpNum);
|
|
|
|
switch (ExtraCode[0]) {
|
2012-05-19 08:51:56 +08:00
|
|
|
default:
|
2012-06-22 01:14:46 +08:00
|
|
|
// See if this is a generic print operand
|
|
|
|
return AsmPrinter::PrintAsmOperand(MI,OpNum,AsmVariant,ExtraCode,O);
|
2012-05-19 08:51:56 +08:00
|
|
|
case 'X': // hex const int
|
|
|
|
if ((MO.getType()) != MachineOperand::MO_Immediate)
|
|
|
|
return true;
|
2015-05-24 00:53:07 +08:00
|
|
|
O << "0x" << Twine::utohexstr(MO.getImm());
|
2012-05-19 08:51:56 +08:00
|
|
|
return false;
|
|
|
|
case 'x': // hex const int (low 16 bits)
|
|
|
|
if ((MO.getType()) != MachineOperand::MO_Immediate)
|
|
|
|
return true;
|
2015-05-24 00:53:07 +08:00
|
|
|
O << "0x" << Twine::utohexstr(MO.getImm() & 0xffff);
|
2012-05-19 08:51:56 +08:00
|
|
|
return false;
|
|
|
|
case 'd': // decimal const int
|
|
|
|
if ((MO.getType()) != MachineOperand::MO_Immediate)
|
|
|
|
return true;
|
|
|
|
O << MO.getImm();
|
|
|
|
return false;
|
2012-05-31 03:05:19 +08:00
|
|
|
case 'm': // decimal const int minus 1
|
|
|
|
if ((MO.getType()) != MachineOperand::MO_Immediate)
|
|
|
|
return true;
|
|
|
|
O << MO.getImm() - 1;
|
|
|
|
return false;
|
2018-02-07 20:36:39 +08:00
|
|
|
case 'y': // exact log2
|
|
|
|
if ((MO.getType()) != MachineOperand::MO_Immediate)
|
|
|
|
return true;
|
|
|
|
if (!isPowerOf2_64(MO.getImm()))
|
|
|
|
return true;
|
|
|
|
O << Log2_64(MO.getImm());
|
|
|
|
return false;
|
2017-08-04 06:12:30 +08:00
|
|
|
case 'z':
|
2012-06-29 04:46:26 +08:00
|
|
|
// $0 if zero, regular printing otherwise
|
2014-11-06 22:25:42 +08:00
|
|
|
if (MO.getType() == MachineOperand::MO_Immediate && MO.getImm() == 0) {
|
2012-06-28 09:33:40 +08:00
|
|
|
O << "$0";
|
2014-11-06 22:25:42 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// If not, call printOperand as normal.
|
|
|
|
break;
|
Mips specific inline asm operand modifier 'L'.
Low order register of a double word register operand. Operands
are defined by the name of the variable they are marked with in
the inline assembler code. This is a way to specify that the
operand just refers to the low order register for that variable.
It is the opposite of modifier 'D' which specifies the high order
register.
Example:
main()
{
long long ll_input = 0x1111222233334444LL;
long long ll_val = 3;
int i_result = 0;
__asm__ __volatile__(
"or %0, %L1, %2"
: "=r" (i_result)
: "r" (ll_input), "r" (ll_val));
}
Which results in:
lui $2, %hi(_gp_disp)
addiu $2, $2, %lo(_gp_disp)
addiu $sp, $sp, -8
addu $2, $2, $25
sw $2, 0($sp)
lui $2, 13107
ori $3, $2, 17476 <-- Low 32 bits of ll_input
lui $2, 4369
ori $4, $2, 8738 <-- High 32 bits of ll_input
addiu $5, $zero, 3 <-- Low 32 bits of ll_val
addiu $2, $zero, 0 <-- High 32 bits of ll_val
#APP
or $3, $4, $5 <-- or i_result, high 32 ll_input, low 32 of ll_val
#NO_APP
addiu $sp, $sp, 8
jr $ra
If not direction is done for the long long for 32 bit variables results
in using the low 32 bits as ll_val shows.
There is an existing bug if 'L' or 'D' is used for the destination register
for 32 bit long longs in that the target value will be updated incorrectly
for the non-specified part unless explicitly set within the inline asm code.
llvm-svn: 160028
2012-07-11 06:41:20 +08:00
|
|
|
case 'D': // Second part of a double word register operand
|
|
|
|
case 'L': // Low order register of a double word register operand
|
2012-07-18 14:41:36 +08:00
|
|
|
case 'M': // High order register of a double word register operand
|
Mips specific inline asm operand modifier 'L'.
Low order register of a double word register operand. Operands
are defined by the name of the variable they are marked with in
the inline assembler code. This is a way to specify that the
operand just refers to the low order register for that variable.
It is the opposite of modifier 'D' which specifies the high order
register.
Example:
main()
{
long long ll_input = 0x1111222233334444LL;
long long ll_val = 3;
int i_result = 0;
__asm__ __volatile__(
"or %0, %L1, %2"
: "=r" (i_result)
: "r" (ll_input), "r" (ll_val));
}
Which results in:
lui $2, %hi(_gp_disp)
addiu $2, $2, %lo(_gp_disp)
addiu $sp, $sp, -8
addu $2, $2, $25
sw $2, 0($sp)
lui $2, 13107
ori $3, $2, 17476 <-- Low 32 bits of ll_input
lui $2, 4369
ori $4, $2, 8738 <-- High 32 bits of ll_input
addiu $5, $zero, 3 <-- Low 32 bits of ll_val
addiu $2, $zero, 0 <-- High 32 bits of ll_val
#APP
or $3, $4, $5 <-- or i_result, high 32 ll_input, low 32 of ll_val
#NO_APP
addiu $sp, $sp, 8
jr $ra
If not direction is done for the long long for 32 bit variables results
in using the low 32 bits as ll_val shows.
There is an existing bug if 'L' or 'D' is used for the destination register
for 32 bit long longs in that the target value will be updated incorrectly
for the non-specified part unless explicitly set within the inline asm code.
llvm-svn: 160028
2012-07-11 06:41:20 +08:00
|
|
|
{
|
2012-07-06 07:58:21 +08:00
|
|
|
if (OpNum == 0)
|
|
|
|
return true;
|
|
|
|
const MachineOperand &FlagsOP = MI->getOperand(OpNum - 1);
|
|
|
|
if (!FlagsOP.isImm())
|
|
|
|
return true;
|
|
|
|
unsigned Flags = FlagsOP.getImm();
|
|
|
|
unsigned NumVals = InlineAsm::getNumOperandRegisters(Flags);
|
2012-07-06 10:44:22 +08:00
|
|
|
// Number of registers represented by this operand. We are looking
|
|
|
|
// for 2 for 32 bit mode and 1 for 64 bit mode.
|
2012-07-06 07:58:21 +08:00
|
|
|
if (NumVals != 2) {
|
2012-07-06 10:44:22 +08:00
|
|
|
if (Subtarget->isGP64bit() && NumVals == 1 && MO.isReg()) {
|
2012-07-06 07:58:21 +08:00
|
|
|
unsigned Reg = MO.getReg();
|
|
|
|
O << '$' << MipsInstPrinter::getRegisterName(Reg);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2012-07-12 05:41:49 +08:00
|
|
|
|
|
|
|
unsigned RegOp = OpNum;
|
|
|
|
if (!Subtarget->isGP64bit()){
|
2016-11-18 19:53:36 +08:00
|
|
|
// Endianness reverses which register holds the high or low value
|
2012-07-18 14:41:36 +08:00
|
|
|
// between M and L.
|
Mips specific inline asm operand modifier 'L'.
Low order register of a double word register operand. Operands
are defined by the name of the variable they are marked with in
the inline assembler code. This is a way to specify that the
operand just refers to the low order register for that variable.
It is the opposite of modifier 'D' which specifies the high order
register.
Example:
main()
{
long long ll_input = 0x1111222233334444LL;
long long ll_val = 3;
int i_result = 0;
__asm__ __volatile__(
"or %0, %L1, %2"
: "=r" (i_result)
: "r" (ll_input), "r" (ll_val));
}
Which results in:
lui $2, %hi(_gp_disp)
addiu $2, $2, %lo(_gp_disp)
addiu $sp, $sp, -8
addu $2, $2, $25
sw $2, 0($sp)
lui $2, 13107
ori $3, $2, 17476 <-- Low 32 bits of ll_input
lui $2, 4369
ori $4, $2, 8738 <-- High 32 bits of ll_input
addiu $5, $zero, 3 <-- Low 32 bits of ll_val
addiu $2, $zero, 0 <-- High 32 bits of ll_val
#APP
or $3, $4, $5 <-- or i_result, high 32 ll_input, low 32 of ll_val
#NO_APP
addiu $sp, $sp, 8
jr $ra
If not direction is done for the long long for 32 bit variables results
in using the low 32 bits as ll_val shows.
There is an existing bug if 'L' or 'D' is used for the destination register
for 32 bit long longs in that the target value will be updated incorrectly
for the non-specified part unless explicitly set within the inline asm code.
llvm-svn: 160028
2012-07-11 06:41:20 +08:00
|
|
|
switch(ExtraCode[0]) {
|
2012-07-18 14:41:36 +08:00
|
|
|
case 'M':
|
|
|
|
RegOp = (Subtarget->isLittle()) ? OpNum + 1 : OpNum;
|
Mips specific inline asm operand modifier 'L'.
Low order register of a double word register operand. Operands
are defined by the name of the variable they are marked with in
the inline assembler code. This is a way to specify that the
operand just refers to the low order register for that variable.
It is the opposite of modifier 'D' which specifies the high order
register.
Example:
main()
{
long long ll_input = 0x1111222233334444LL;
long long ll_val = 3;
int i_result = 0;
__asm__ __volatile__(
"or %0, %L1, %2"
: "=r" (i_result)
: "r" (ll_input), "r" (ll_val));
}
Which results in:
lui $2, %hi(_gp_disp)
addiu $2, $2, %lo(_gp_disp)
addiu $sp, $sp, -8
addu $2, $2, $25
sw $2, 0($sp)
lui $2, 13107
ori $3, $2, 17476 <-- Low 32 bits of ll_input
lui $2, 4369
ori $4, $2, 8738 <-- High 32 bits of ll_input
addiu $5, $zero, 3 <-- Low 32 bits of ll_val
addiu $2, $zero, 0 <-- High 32 bits of ll_val
#APP
or $3, $4, $5 <-- or i_result, high 32 ll_input, low 32 of ll_val
#NO_APP
addiu $sp, $sp, 8
jr $ra
If not direction is done for the long long for 32 bit variables results
in using the low 32 bits as ll_val shows.
There is an existing bug if 'L' or 'D' is used for the destination register
for 32 bit long longs in that the target value will be updated incorrectly
for the non-specified part unless explicitly set within the inline asm code.
llvm-svn: 160028
2012-07-11 06:41:20 +08:00
|
|
|
break;
|
|
|
|
case 'L':
|
2012-07-18 14:41:36 +08:00
|
|
|
RegOp = (Subtarget->isLittle()) ? OpNum : OpNum + 1;
|
|
|
|
break;
|
|
|
|
case 'D': // Always the second part
|
|
|
|
RegOp = OpNum + 1;
|
Mips specific inline asm operand modifier 'L'.
Low order register of a double word register operand. Operands
are defined by the name of the variable they are marked with in
the inline assembler code. This is a way to specify that the
operand just refers to the low order register for that variable.
It is the opposite of modifier 'D' which specifies the high order
register.
Example:
main()
{
long long ll_input = 0x1111222233334444LL;
long long ll_val = 3;
int i_result = 0;
__asm__ __volatile__(
"or %0, %L1, %2"
: "=r" (i_result)
: "r" (ll_input), "r" (ll_val));
}
Which results in:
lui $2, %hi(_gp_disp)
addiu $2, $2, %lo(_gp_disp)
addiu $sp, $sp, -8
addu $2, $2, $25
sw $2, 0($sp)
lui $2, 13107
ori $3, $2, 17476 <-- Low 32 bits of ll_input
lui $2, 4369
ori $4, $2, 8738 <-- High 32 bits of ll_input
addiu $5, $zero, 3 <-- Low 32 bits of ll_val
addiu $2, $zero, 0 <-- High 32 bits of ll_val
#APP
or $3, $4, $5 <-- or i_result, high 32 ll_input, low 32 of ll_val
#NO_APP
addiu $sp, $sp, 8
jr $ra
If not direction is done for the long long for 32 bit variables results
in using the low 32 bits as ll_val shows.
There is an existing bug if 'L' or 'D' is used for the destination register
for 32 bit long longs in that the target value will be updated incorrectly
for the non-specified part unless explicitly set within the inline asm code.
llvm-svn: 160028
2012-07-11 06:41:20 +08:00
|
|
|
}
|
|
|
|
if (RegOp >= MI->getNumOperands())
|
|
|
|
return true;
|
|
|
|
const MachineOperand &MO = MI->getOperand(RegOp);
|
|
|
|
if (!MO.isReg())
|
|
|
|
return true;
|
|
|
|
unsigned Reg = MO.getReg();
|
|
|
|
O << '$' << MipsInstPrinter::getRegisterName(Reg);
|
|
|
|
return false;
|
2012-07-06 07:58:21 +08:00
|
|
|
}
|
2012-05-11 05:48:22 +08:00
|
|
|
}
|
2013-11-12 20:56:01 +08:00
|
|
|
case 'w':
|
|
|
|
// Print MSA registers for the 'f' constraint
|
|
|
|
// In LLVM, the 'w' modifier doesn't need to do anything.
|
|
|
|
// We can just call printOperand as normal.
|
|
|
|
break;
|
2012-07-06 10:44:22 +08:00
|
|
|
}
|
|
|
|
}
|
2008-08-03 03:42:36 +08:00
|
|
|
|
2012-05-11 05:48:22 +08:00
|
|
|
printOperand(MI, OpNum, O);
|
2008-08-03 03:42:36 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-06-21 08:40:49 +08:00
|
|
|
bool MipsAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
|
|
|
|
unsigned OpNum, unsigned AsmVariant,
|
|
|
|
const char *ExtraCode,
|
|
|
|
raw_ostream &O) {
|
2015-03-24 19:26:34 +08:00
|
|
|
assert(OpNum + 1 < MI->getNumOperands() && "Insufficient operands");
|
|
|
|
const MachineOperand &BaseMO = MI->getOperand(OpNum);
|
|
|
|
const MachineOperand &OffsetMO = MI->getOperand(OpNum + 1);
|
|
|
|
assert(BaseMO.isReg() && "Unexpected base pointer for inline asm memory operand.");
|
|
|
|
assert(OffsetMO.isImm() && "Unexpected offset for inline asm memory operand.");
|
|
|
|
int Offset = OffsetMO.getImm();
|
|
|
|
|
2018-02-07 20:36:33 +08:00
|
|
|
// Currently we are expecting either no ExtraCode or 'D','M','L'.
|
Mips specific inline asm operand modifier 'D'
Modifier 'D' is to use the second word of a double integer.
We had previously implemented the pure register varient of
the modifier and this patch implements the memory reference.
#include "stdio.h"
int b[8] = {0,1,2,3,4,5,6,7};
void main()
{
int i;
// The first word. Notice, no 'D'
{asm (
"lw %0,%1;"
: "=r" (i)
: "m" (*(b+4))
);}
printf("%d\n",i);
// The second word
{asm (
"lw %0,%D1;"
: "=r" (i)
: "m" (*(b+4))
);}
printf("%d\n",i);
}
llvm-svn: 179135
2013-04-10 07:19:50 +08:00
|
|
|
if (ExtraCode) {
|
2018-02-07 20:36:33 +08:00
|
|
|
switch (ExtraCode[0]) {
|
|
|
|
case 'D':
|
2015-03-24 19:26:34 +08:00
|
|
|
Offset += 4;
|
2018-02-07 20:36:33 +08:00
|
|
|
break;
|
|
|
|
case 'M':
|
|
|
|
if (Subtarget->isLittle())
|
|
|
|
Offset += 4;
|
|
|
|
break;
|
|
|
|
case 'L':
|
|
|
|
if (!Subtarget->isLittle())
|
|
|
|
Offset += 4;
|
|
|
|
break;
|
|
|
|
default:
|
Mips specific inline asm operand modifier 'D'
Modifier 'D' is to use the second word of a double integer.
We had previously implemented the pure register varient of
the modifier and this patch implements the memory reference.
#include "stdio.h"
int b[8] = {0,1,2,3,4,5,6,7};
void main()
{
int i;
// The first word. Notice, no 'D'
{asm (
"lw %0,%1;"
: "=r" (i)
: "m" (*(b+4))
);}
printf("%d\n",i);
// The second word
{asm (
"lw %0,%D1;"
: "=r" (i)
: "m" (*(b+4))
);}
printf("%d\n",i);
}
llvm-svn: 179135
2013-04-10 07:19:50 +08:00
|
|
|
return true; // Unknown modifier.
|
2018-02-07 20:36:33 +08:00
|
|
|
}
|
Mips specific inline asm operand modifier 'D'
Modifier 'D' is to use the second word of a double integer.
We had previously implemented the pure register varient of
the modifier and this patch implements the memory reference.
#include "stdio.h"
int b[8] = {0,1,2,3,4,5,6,7};
void main()
{
int i;
// The first word. Notice, no 'D'
{asm (
"lw %0,%1;"
: "=r" (i)
: "m" (*(b+4))
);}
printf("%d\n",i);
// The second word
{asm (
"lw %0,%D1;"
: "=r" (i)
: "m" (*(b+4))
);}
printf("%d\n",i);
}
llvm-svn: 179135
2013-04-10 07:19:50 +08:00
|
|
|
}
|
2012-02-28 15:46:26 +08:00
|
|
|
|
2018-02-07 20:36:33 +08:00
|
|
|
O << Offset << "($" << MipsInstPrinter::getRegisterName(BaseMO.getReg())
|
|
|
|
<< ")";
|
2012-06-28 09:33:40 +08:00
|
|
|
|
2011-06-21 08:40:49 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-04-04 12:47:45 +08:00
|
|
|
void MipsAsmPrinter::printOperand(const MachineInstr *MI, int opNum,
|
|
|
|
raw_ostream &O) {
|
2007-06-06 15:42:06 +08:00
|
|
|
const MachineOperand &MO = MI->getOperand(opNum);
|
2007-11-05 11:02:32 +08:00
|
|
|
bool closeP = false;
|
2009-09-02 01:27:58 +08:00
|
|
|
|
|
|
|
if (MO.getTargetFlags())
|
2009-08-28 03:57:56 +08:00
|
|
|
closeP = true;
|
2009-09-02 01:27:58 +08:00
|
|
|
|
|
|
|
switch(MO.getTargetFlags()) {
|
|
|
|
case MipsII::MO_GPREL: O << "%gp_rel("; break;
|
|
|
|
case MipsII::MO_GOT_CALL: O << "%call16("; break;
|
2011-04-02 05:41:06 +08:00
|
|
|
case MipsII::MO_GOT: O << "%got("; break;
|
|
|
|
case MipsII::MO_ABS_HI: O << "%hi("; break;
|
|
|
|
case MipsII::MO_ABS_LO: O << "%lo("; break;
|
2017-01-27 19:36:52 +08:00
|
|
|
case MipsII::MO_HIGHER: O << "%higher("; break;
|
|
|
|
case MipsII::MO_HIGHEST: O << "%highest(("; break;
|
2011-05-31 10:53:58 +08:00
|
|
|
case MipsII::MO_TLSGD: O << "%tlsgd("; break;
|
|
|
|
case MipsII::MO_GOTTPREL: O << "%gottprel("; break;
|
|
|
|
case MipsII::MO_TPREL_HI: O << "%tprel_hi("; break;
|
|
|
|
case MipsII::MO_TPREL_LO: O << "%tprel_lo("; break;
|
2011-09-22 11:09:07 +08:00
|
|
|
case MipsII::MO_GPOFF_HI: O << "%hi(%neg(%gp_rel("; break;
|
|
|
|
case MipsII::MO_GPOFF_LO: O << "%lo(%neg(%gp_rel("; break;
|
|
|
|
case MipsII::MO_GOT_DISP: O << "%got_disp("; break;
|
|
|
|
case MipsII::MO_GOT_PAGE: O << "%got_page("; break;
|
|
|
|
case MipsII::MO_GOT_OFST: O << "%got_ofst("; break;
|
2007-06-06 15:42:06 +08:00
|
|
|
}
|
2009-09-02 01:27:58 +08:00
|
|
|
|
2009-09-14 04:31:40 +08:00
|
|
|
switch (MO.getType()) {
|
2007-06-06 15:42:06 +08:00
|
|
|
case MachineOperand::MO_Register:
|
2011-07-08 07:56:50 +08:00
|
|
|
O << '$'
|
2011-11-07 04:37:06 +08:00
|
|
|
<< StringRef(MipsInstPrinter::getRegisterName(MO.getReg())).lower();
|
2007-06-06 15:42:06 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case MachineOperand::MO_Immediate:
|
2011-05-25 05:22:21 +08:00
|
|
|
O << MO.getImm();
|
2007-06-06 15:42:06 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case MachineOperand::MO_MachineBasicBlock:
|
2015-06-09 08:31:39 +08:00
|
|
|
MO.getMBB()->getSymbol()->print(O, MAI);
|
2007-06-06 15:42:06 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
case MachineOperand::MO_GlobalAddress:
|
2015-06-09 08:31:39 +08:00
|
|
|
getSymbol(MO.getGlobal())->print(O, MAI);
|
2007-06-06 15:42:06 +08:00
|
|
|
break;
|
|
|
|
|
2011-03-05 04:01:52 +08:00
|
|
|
case MachineOperand::MO_BlockAddress: {
|
2012-06-15 05:10:56 +08:00
|
|
|
MCSymbol *BA = GetBlockAddressSymbol(MO.getBlockAddress());
|
2011-03-05 04:01:52 +08:00
|
|
|
O << BA->getName();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-06-06 15:42:06 +08:00
|
|
|
case MachineOperand::MO_ConstantPoolIndex:
|
2015-07-16 14:11:10 +08:00
|
|
|
O << getDataLayout().getPrivateGlobalPrefix() << "CPI"
|
2007-12-31 07:10:15 +08:00
|
|
|
<< getFunctionNumber() << "_" << MO.getIndex();
|
2009-11-19 14:06:13 +08:00
|
|
|
if (MO.getOffset())
|
|
|
|
O << "+" << MO.getOffset();
|
2007-06-06 15:42:06 +08:00
|
|
|
break;
|
2011-03-05 01:51:39 +08:00
|
|
|
|
2007-06-06 15:42:06 +08:00
|
|
|
default:
|
2009-07-15 00:55:14 +08:00
|
|
|
llvm_unreachable("<unknown operand type>");
|
2007-06-06 15:42:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (closeP) O << ")";
|
|
|
|
}
|
|
|
|
|
|
|
|
void MipsAsmPrinter::
|
2011-07-08 04:54:20 +08:00
|
|
|
printMemOperand(const MachineInstr *MI, int opNum, raw_ostream &O) {
|
2011-03-05 01:51:39 +08:00
|
|
|
// Load/Store memory operands -- imm($reg)
|
|
|
|
// If PIC target the target is loaded as the
|
2007-11-05 11:02:32 +08:00
|
|
|
// pattern lw $25,%call16($28)
|
2014-11-20 00:44:02 +08:00
|
|
|
|
|
|
|
// opNum can be invalid if instruction has reglist as operand.
|
|
|
|
// MemOperand is always last operand of instruction (base + offset).
|
|
|
|
switch (MI->getOpcode()) {
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
case Mips::SWM32_MM:
|
|
|
|
case Mips::LWM32_MM:
|
|
|
|
opNum = MI->getNumOperands() - 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-04-04 12:47:45 +08:00
|
|
|
printOperand(MI, opNum+1, O);
|
2011-07-08 02:57:00 +08:00
|
|
|
O << "(";
|
|
|
|
printOperand(MI, opNum, O);
|
2007-06-06 15:42:06 +08:00
|
|
|
O << ")";
|
|
|
|
}
|
|
|
|
|
2011-07-08 04:54:20 +08:00
|
|
|
void MipsAsmPrinter::
|
|
|
|
printMemOperandEA(const MachineInstr *MI, int opNum, raw_ostream &O) {
|
|
|
|
// when using stack locations for not load/store instructions
|
|
|
|
// print the same way as all normal 3 operand instructions.
|
|
|
|
printOperand(MI, opNum, O);
|
|
|
|
O << ", ";
|
|
|
|
printOperand(MI, opNum+1, O);
|
|
|
|
}
|
|
|
|
|
2016-09-09 19:06:01 +08:00
|
|
|
void MipsAsmPrinter::
|
|
|
|
printFCCOperand(const MachineInstr *MI, int opNum, raw_ostream &O,
|
|
|
|
const char *Modifier) {
|
|
|
|
const MachineOperand &MO = MI->getOperand(opNum);
|
|
|
|
O << Mips::MipsFCCToString((Mips::CondCode)MO.getImm());
|
|
|
|
}
|
|
|
|
|
2014-11-20 00:44:02 +08:00
|
|
|
void MipsAsmPrinter::
|
|
|
|
printRegisterList(const MachineInstr *MI, int opNum, raw_ostream &O) {
|
|
|
|
for (int i = opNum, e = MI->getNumOperands(); i != e; ++i) {
|
|
|
|
if (i != opNum) O << ", ";
|
|
|
|
printOperand(MI, i, O);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-01 06:06:26 +08:00
|
|
|
void MipsAsmPrinter::EmitStartOfAsmFile(Module &M) {
|
2016-05-06 22:37:24 +08:00
|
|
|
MipsTargetStreamer &TS = getTargetStreamer();
|
|
|
|
|
|
|
|
// MipsTargetStreamer has an initialization order problem when emitting an
|
|
|
|
// object file directly (see MipsTargetELFStreamer for full details). Work
|
|
|
|
// around it by re-initializing the PIC state here.
|
2016-05-18 19:58:50 +08:00
|
|
|
TS.setPic(OutContext.getObjectFileInfo()->isPositionIndependent());
|
2015-02-18 09:01:57 +08:00
|
|
|
|
|
|
|
// Compute MIPS architecture attributes based on the default subtarget
|
|
|
|
// that we'd have constructed. Module level directives aren't LTO
|
|
|
|
// clean anyhow.
|
|
|
|
// FIXME: For ifunc related functions we could iterate over and look
|
|
|
|
// for a feature string that doesn't match the default one.
|
2015-09-16 00:17:27 +08:00
|
|
|
const Triple &TT = TM.getTargetTriple();
|
2015-06-10 20:11:26 +08:00
|
|
|
StringRef CPU = MIPS_MC::selectMipsCPU(TT, TM.getTargetCPU());
|
2015-02-18 09:01:57 +08:00
|
|
|
StringRef FS = TM.getTargetFeatureString();
|
|
|
|
const MipsTargetMachine &MTM = static_cast<const MipsTargetMachine &>(TM);
|
2017-08-15 05:49:38 +08:00
|
|
|
const MipsSubtarget STI(TT, CPU, FS, MTM.isLittleEndian(), MTM, 0);
|
2015-02-18 09:01:57 +08:00
|
|
|
|
|
|
|
bool IsABICalls = STI.isABICalls();
|
|
|
|
const MipsABIInfo &ABI = MTM.getABI();
|
2014-04-16 21:58:57 +08:00
|
|
|
if (IsABICalls) {
|
2016-05-06 22:37:24 +08:00
|
|
|
TS.emitDirectiveAbiCalls();
|
2014-04-16 21:58:57 +08:00
|
|
|
// FIXME: This condition should be a lot more complicated that it is here.
|
|
|
|
// Ideally it should test for properties of the ABI and not the ABI
|
|
|
|
// itself.
|
|
|
|
// For the moment, I'm only correcting enough to make MIPS-IV work.
|
2017-01-27 19:36:52 +08:00
|
|
|
if (!isPositionIndependent() && STI.hasSym32())
|
2016-05-06 22:37:24 +08:00
|
|
|
TS.emitDirectiveOptionPic0();
|
2014-04-16 21:58:57 +08:00
|
|
|
}
|
2013-06-19 03:47:15 +08:00
|
|
|
|
2008-07-14 22:42:54 +08:00
|
|
|
// Tell the assembler which ABI we are using
|
2014-01-27 09:33:33 +08:00
|
|
|
std::string SectionName = std::string(".mdebug.") + getCurrentABIString();
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->SwitchSection(
|
2015-01-30 01:33:21 +08:00
|
|
|
OutContext.getELFSection(SectionName, ELF::SHT_PROGBITS, 0));
|
2008-07-14 22:42:54 +08:00
|
|
|
|
2014-04-16 23:48:55 +08:00
|
|
|
// NaN: At the moment we only support:
|
|
|
|
// 1. .nan legacy (default)
|
|
|
|
// 2. .nan 2008
|
2016-05-06 22:37:24 +08:00
|
|
|
STI.isNaN2008() ? TS.emitDirectiveNaN2008()
|
|
|
|
: TS.emitDirectiveNaNLegacy();
|
2014-04-16 23:48:55 +08:00
|
|
|
|
2008-07-14 22:42:54 +08:00
|
|
|
// TODO: handle O64 ABI
|
|
|
|
|
2016-05-06 22:37:24 +08:00
|
|
|
TS.updateABIInfo(STI);
|
2014-07-21 23:25:24 +08:00
|
|
|
|
|
|
|
// We should always emit a '.module fp=...' but binutils 2.24 does not accept
|
|
|
|
// it. We therefore emit it when it contradicts the ABI defaults (-mfpxx or
|
|
|
|
// -mfp64) and omit it otherwise.
|
2015-02-18 09:01:57 +08:00
|
|
|
if (ABI.IsO32() && (STI.isABI_FPXX() || STI.isFP64bit()))
|
2016-05-06 22:37:24 +08:00
|
|
|
TS.emitDirectiveModuleFP();
|
2014-07-21 23:25:24 +08:00
|
|
|
|
|
|
|
// We should always emit a '.module [no]oddspreg' but binutils 2.24 does not
|
|
|
|
// accept it. We therefore emit it when it contradicts the default or an
|
|
|
|
// option has changed the default (i.e. FPXX) and omit it otherwise.
|
2015-02-18 09:01:57 +08:00
|
|
|
if (ABI.IsO32() && (!STI.useOddSPReg() || STI.isABI_FPXX()))
|
2016-05-06 22:37:24 +08:00
|
|
|
TS.emitDirectiveModuleOddSPReg();
|
2014-02-15 03:16:39 +08:00
|
|
|
}
|
|
|
|
|
2015-02-20 03:52:25 +08:00
|
|
|
void MipsAsmPrinter::emitInlineAsmStart() const {
|
2014-12-17 18:56:16 +08:00
|
|
|
MipsTargetStreamer &TS = getTargetStreamer();
|
|
|
|
|
2015-01-09 23:00:30 +08:00
|
|
|
// GCC's choice of assembler options for inline assembly code ('at', 'macro'
|
|
|
|
// and 'reorder') is different from LLVM's choice for generated code ('noat',
|
|
|
|
// 'nomacro' and 'noreorder').
|
|
|
|
// In order to maintain compatibility with inline assembly code which depends
|
|
|
|
// on GCC's assembler options being used, we have to switch to those options
|
|
|
|
// for the duration of the inline assembly block and then switch back.
|
2014-12-17 18:56:16 +08:00
|
|
|
TS.emitDirectiveSetPush();
|
|
|
|
TS.emitDirectiveSetAt();
|
|
|
|
TS.emitDirectiveSetMacro();
|
|
|
|
TS.emitDirectiveSetReorder();
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->AddBlankLine();
|
2014-12-17 18:56:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void MipsAsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
|
|
|
|
const MCSubtargetInfo *EndInfo) const {
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->AddBlankLine();
|
2014-12-17 18:56:16 +08:00
|
|
|
getTargetStreamer().emitDirectiveSetPop();
|
|
|
|
}
|
|
|
|
|
2015-02-21 16:48:22 +08:00
|
|
|
void MipsAsmPrinter::EmitJal(const MCSubtargetInfo &STI, MCSymbol *Symbol) {
|
2014-02-15 03:16:39 +08:00
|
|
|
MCInst I;
|
|
|
|
I.setOpcode(Mips::JAL);
|
|
|
|
I.addOperand(
|
2015-05-30 09:25:56 +08:00
|
|
|
MCOperand::createExpr(MCSymbolRefExpr::create(Symbol, OutContext)));
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->EmitInstruction(I, STI);
|
2014-02-15 03:16:39 +08:00
|
|
|
}
|
|
|
|
|
2015-02-21 16:48:22 +08:00
|
|
|
void MipsAsmPrinter::EmitInstrReg(const MCSubtargetInfo &STI, unsigned Opcode,
|
|
|
|
unsigned Reg) {
|
2014-02-15 03:16:39 +08:00
|
|
|
MCInst I;
|
|
|
|
I.setOpcode(Opcode);
|
2015-05-14 02:37:00 +08:00
|
|
|
I.addOperand(MCOperand::createReg(Reg));
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->EmitInstruction(I, STI);
|
2014-02-15 03:16:39 +08:00
|
|
|
}
|
|
|
|
|
2015-02-21 16:48:22 +08:00
|
|
|
void MipsAsmPrinter::EmitInstrRegReg(const MCSubtargetInfo &STI,
|
|
|
|
unsigned Opcode, unsigned Reg1,
|
2014-02-15 03:16:39 +08:00
|
|
|
unsigned Reg2) {
|
|
|
|
MCInst I;
|
|
|
|
//
|
|
|
|
// Because of the current td files for Mips32, the operands for MTC1
|
|
|
|
// appear backwards from their normal assembly order. It's not a trivial
|
|
|
|
// change to fix this in the td file so we adjust for it here.
|
|
|
|
//
|
|
|
|
if (Opcode == Mips::MTC1) {
|
|
|
|
unsigned Temp = Reg1;
|
|
|
|
Reg1 = Reg2;
|
|
|
|
Reg2 = Temp;
|
|
|
|
}
|
|
|
|
I.setOpcode(Opcode);
|
2015-05-14 02:37:00 +08:00
|
|
|
I.addOperand(MCOperand::createReg(Reg1));
|
|
|
|
I.addOperand(MCOperand::createReg(Reg2));
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->EmitInstruction(I, STI);
|
2014-02-15 03:16:39 +08:00
|
|
|
}
|
|
|
|
|
2015-02-21 16:48:22 +08:00
|
|
|
void MipsAsmPrinter::EmitInstrRegRegReg(const MCSubtargetInfo &STI,
|
|
|
|
unsigned Opcode, unsigned Reg1,
|
2014-02-15 03:16:39 +08:00
|
|
|
unsigned Reg2, unsigned Reg3) {
|
|
|
|
MCInst I;
|
|
|
|
I.setOpcode(Opcode);
|
2015-05-14 02:37:00 +08:00
|
|
|
I.addOperand(MCOperand::createReg(Reg1));
|
|
|
|
I.addOperand(MCOperand::createReg(Reg2));
|
|
|
|
I.addOperand(MCOperand::createReg(Reg3));
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->EmitInstruction(I, STI);
|
2014-02-15 03:16:39 +08:00
|
|
|
}
|
|
|
|
|
2015-02-21 16:48:22 +08:00
|
|
|
void MipsAsmPrinter::EmitMovFPIntPair(const MCSubtargetInfo &STI,
|
|
|
|
unsigned MovOpc, unsigned Reg1,
|
2014-02-15 03:16:39 +08:00
|
|
|
unsigned Reg2, unsigned FPReg1,
|
|
|
|
unsigned FPReg2, bool LE) {
|
|
|
|
if (!LE) {
|
|
|
|
unsigned temp = Reg1;
|
|
|
|
Reg1 = Reg2;
|
|
|
|
Reg2 = temp;
|
|
|
|
}
|
2015-02-21 16:48:22 +08:00
|
|
|
EmitInstrRegReg(STI, MovOpc, Reg1, FPReg1);
|
|
|
|
EmitInstrRegReg(STI, MovOpc, Reg2, FPReg2);
|
2014-02-15 03:16:39 +08:00
|
|
|
}
|
|
|
|
|
2015-02-21 16:48:22 +08:00
|
|
|
void MipsAsmPrinter::EmitSwapFPIntParams(const MCSubtargetInfo &STI,
|
|
|
|
Mips16HardFloatInfo::FPParamVariant PV,
|
2014-02-15 03:16:39 +08:00
|
|
|
bool LE, bool ToFP) {
|
|
|
|
using namespace Mips16HardFloatInfo;
|
2017-08-04 06:12:30 +08:00
|
|
|
|
2014-02-15 03:16:39 +08:00
|
|
|
unsigned MovOpc = ToFP ? Mips::MTC1 : Mips::MFC1;
|
|
|
|
switch (PV) {
|
|
|
|
case FSig:
|
2015-02-21 16:48:22 +08:00
|
|
|
EmitInstrRegReg(STI, MovOpc, Mips::A0, Mips::F12);
|
2014-02-15 03:16:39 +08:00
|
|
|
break;
|
|
|
|
case FFSig:
|
2015-02-21 16:48:22 +08:00
|
|
|
EmitMovFPIntPair(STI, MovOpc, Mips::A0, Mips::A1, Mips::F12, Mips::F14, LE);
|
2014-02-15 03:16:39 +08:00
|
|
|
break;
|
|
|
|
case FDSig:
|
2015-02-21 16:48:22 +08:00
|
|
|
EmitInstrRegReg(STI, MovOpc, Mips::A0, Mips::F12);
|
|
|
|
EmitMovFPIntPair(STI, MovOpc, Mips::A2, Mips::A3, Mips::F14, Mips::F15, LE);
|
2014-02-15 03:16:39 +08:00
|
|
|
break;
|
|
|
|
case DSig:
|
2015-02-21 16:48:22 +08:00
|
|
|
EmitMovFPIntPair(STI, MovOpc, Mips::A0, Mips::A1, Mips::F12, Mips::F13, LE);
|
2014-02-15 03:16:39 +08:00
|
|
|
break;
|
|
|
|
case DDSig:
|
2015-02-21 16:48:22 +08:00
|
|
|
EmitMovFPIntPair(STI, MovOpc, Mips::A0, Mips::A1, Mips::F12, Mips::F13, LE);
|
|
|
|
EmitMovFPIntPair(STI, MovOpc, Mips::A2, Mips::A3, Mips::F14, Mips::F15, LE);
|
2014-02-15 03:16:39 +08:00
|
|
|
break;
|
|
|
|
case DFSig:
|
2015-02-21 16:48:22 +08:00
|
|
|
EmitMovFPIntPair(STI, MovOpc, Mips::A0, Mips::A1, Mips::F12, Mips::F13, LE);
|
|
|
|
EmitInstrRegReg(STI, MovOpc, Mips::A2, Mips::F14);
|
2014-02-15 03:16:39 +08:00
|
|
|
break;
|
|
|
|
case NoSig:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-21 16:48:22 +08:00
|
|
|
void MipsAsmPrinter::EmitSwapFPIntRetval(
|
|
|
|
const MCSubtargetInfo &STI, Mips16HardFloatInfo::FPReturnVariant RV,
|
|
|
|
bool LE) {
|
2014-02-15 03:16:39 +08:00
|
|
|
using namespace Mips16HardFloatInfo;
|
2017-08-04 06:12:30 +08:00
|
|
|
|
2014-02-15 03:16:39 +08:00
|
|
|
unsigned MovOpc = Mips::MFC1;
|
|
|
|
switch (RV) {
|
|
|
|
case FRet:
|
2015-02-21 16:48:22 +08:00
|
|
|
EmitInstrRegReg(STI, MovOpc, Mips::V0, Mips::F0);
|
2014-02-15 03:16:39 +08:00
|
|
|
break;
|
|
|
|
case DRet:
|
2015-02-21 16:48:22 +08:00
|
|
|
EmitMovFPIntPair(STI, MovOpc, Mips::V0, Mips::V1, Mips::F0, Mips::F1, LE);
|
2014-02-15 03:16:39 +08:00
|
|
|
break;
|
|
|
|
case CFRet:
|
2015-02-21 16:48:22 +08:00
|
|
|
EmitMovFPIntPair(STI, MovOpc, Mips::V0, Mips::V1, Mips::F0, Mips::F1, LE);
|
2014-02-15 03:16:39 +08:00
|
|
|
break;
|
|
|
|
case CDRet:
|
2015-02-21 16:48:22 +08:00
|
|
|
EmitMovFPIntPair(STI, MovOpc, Mips::V0, Mips::V1, Mips::F0, Mips::F1, LE);
|
|
|
|
EmitMovFPIntPair(STI, MovOpc, Mips::A0, Mips::A1, Mips::F2, Mips::F3, LE);
|
2014-02-15 03:16:39 +08:00
|
|
|
break;
|
|
|
|
case NoFPRet:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-01-19 05:20:38 +08:00
|
|
|
|
2014-02-15 03:16:39 +08:00
|
|
|
void MipsAsmPrinter::EmitFPCallStub(
|
|
|
|
const char *Symbol, const Mips16HardFloatInfo::FuncSignature *Signature) {
|
|
|
|
using namespace Mips16HardFloatInfo;
|
2017-08-04 06:12:30 +08:00
|
|
|
|
|
|
|
MCSymbol *MSymbol = OutContext.getOrCreateSymbol(StringRef(Symbol));
|
2015-02-21 16:32:22 +08:00
|
|
|
bool LE = getDataLayout().isLittleEndian();
|
2015-02-21 16:48:22 +08:00
|
|
|
// Construct a local MCSubtargetInfo here.
|
|
|
|
// This is because the MachineFunction won't exist (but have not yet been
|
|
|
|
// freed) and since we're at the global level we can use the default
|
|
|
|
// constructed subtarget.
|
|
|
|
std::unique_ptr<MCSubtargetInfo> STI(TM.getTarget().createMCSubtargetInfo(
|
2015-06-16 21:15:50 +08:00
|
|
|
TM.getTargetTriple().str(), TM.getTargetCPU(),
|
|
|
|
TM.getTargetFeatureString()));
|
2015-02-21 16:48:22 +08:00
|
|
|
|
2014-02-15 03:16:39 +08:00
|
|
|
//
|
|
|
|
// .global xxxx
|
|
|
|
//
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->EmitSymbolAttribute(MSymbol, MCSA_Global);
|
2014-02-15 03:16:39 +08:00
|
|
|
const char *RetType;
|
|
|
|
//
|
|
|
|
// make the comment field identifying the return and parameter
|
|
|
|
// types of the floating point stub
|
|
|
|
// # Stub function to call rettype xxxx (params)
|
|
|
|
//
|
|
|
|
switch (Signature->RetSig) {
|
|
|
|
case FRet:
|
|
|
|
RetType = "float";
|
|
|
|
break;
|
|
|
|
case DRet:
|
|
|
|
RetType = "double";
|
|
|
|
break;
|
|
|
|
case CFRet:
|
|
|
|
RetType = "complex";
|
|
|
|
break;
|
|
|
|
case CDRet:
|
|
|
|
RetType = "double complex";
|
|
|
|
break;
|
|
|
|
case NoFPRet:
|
|
|
|
RetType = "";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
const char *Parms;
|
|
|
|
switch (Signature->ParamSig) {
|
|
|
|
case FSig:
|
|
|
|
Parms = "float";
|
|
|
|
break;
|
|
|
|
case FFSig:
|
|
|
|
Parms = "float, float";
|
|
|
|
break;
|
|
|
|
case FDSig:
|
|
|
|
Parms = "float, double";
|
|
|
|
break;
|
|
|
|
case DSig:
|
|
|
|
Parms = "double";
|
|
|
|
break;
|
|
|
|
case DDSig:
|
|
|
|
Parms = "double, double";
|
|
|
|
break;
|
|
|
|
case DFSig:
|
|
|
|
Parms = "double, float";
|
|
|
|
break;
|
|
|
|
case NoSig:
|
|
|
|
Parms = "";
|
|
|
|
break;
|
|
|
|
}
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->AddComment("\t# Stub function to call " + Twine(RetType) + " " +
|
|
|
|
Twine(Symbol) + " (" + Twine(Parms) + ")");
|
2014-02-15 03:16:39 +08:00
|
|
|
//
|
|
|
|
// probably not necessary but we save and restore the current section state
|
|
|
|
//
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->PushSection();
|
2014-02-15 03:16:39 +08:00
|
|
|
//
|
|
|
|
// .section mips16.call.fpxxxx,"ax",@progbits
|
|
|
|
//
|
2015-05-22 03:20:38 +08:00
|
|
|
MCSectionELF *M = OutContext.getELFSection(
|
2014-02-15 03:16:39 +08:00
|
|
|
".mips16.call.fp." + std::string(Symbol), ELF::SHT_PROGBITS,
|
2015-01-30 01:33:21 +08:00
|
|
|
ELF::SHF_ALLOC | ELF::SHF_EXECINSTR);
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->SwitchSection(M, nullptr);
|
2014-02-15 03:16:39 +08:00
|
|
|
//
|
|
|
|
// .align 2
|
|
|
|
//
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->EmitValueToAlignment(4);
|
2014-02-15 03:16:39 +08:00
|
|
|
MipsTargetStreamer &TS = getTargetStreamer();
|
|
|
|
//
|
|
|
|
// .set nomips16
|
|
|
|
// .set nomicromips
|
|
|
|
//
|
|
|
|
TS.emitDirectiveSetNoMips16();
|
|
|
|
TS.emitDirectiveSetNoMicroMips();
|
|
|
|
//
|
|
|
|
// .ent __call_stub_fp_xxxx
|
2014-07-08 16:59:22 +08:00
|
|
|
// .type __call_stub_fp_xxxx,@function
|
2014-02-15 03:16:39 +08:00
|
|
|
// __call_stub_fp_xxxx:
|
|
|
|
//
|
|
|
|
std::string x = "__call_stub_fp_" + std::string(Symbol);
|
2015-06-02 08:25:12 +08:00
|
|
|
MCSymbolELF *Stub =
|
|
|
|
cast<MCSymbolELF>(OutContext.getOrCreateSymbol(StringRef(x)));
|
2014-02-15 03:16:39 +08:00
|
|
|
TS.emitDirectiveEnt(*Stub);
|
|
|
|
MCSymbol *MType =
|
2015-05-19 02:43:14 +08:00
|
|
|
OutContext.getOrCreateSymbol("__call_stub_fp_" + Twine(Symbol));
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->EmitSymbolAttribute(MType, MCSA_ELF_TypeFunction);
|
|
|
|
OutStreamer->EmitLabel(Stub);
|
2015-02-21 16:32:38 +08:00
|
|
|
|
|
|
|
// Only handle non-pic for now.
|
2016-06-28 01:21:46 +08:00
|
|
|
assert(!isPositionIndependent() &&
|
2015-02-21 16:32:38 +08:00
|
|
|
"should not be here if we are compiling pic");
|
2014-02-15 03:16:39 +08:00
|
|
|
TS.emitDirectiveSetReorder();
|
|
|
|
//
|
|
|
|
// We need to add a MipsMCExpr class to MCTargetDesc to fully implement
|
|
|
|
// stubs without raw text but this current patch is for compiler generated
|
|
|
|
// functions and they all return some value.
|
|
|
|
// The calling sequence for non pic is different in that case and we need
|
|
|
|
// to implement %lo and %hi in order to handle the case of no return value
|
|
|
|
// See the corresponding method in Mips16HardFloat for details.
|
|
|
|
//
|
|
|
|
// mov the return address to S2.
|
|
|
|
// we have no stack space to store it and we are about to make another call.
|
|
|
|
// We need to make sure that the enclosing function knows to save S2
|
|
|
|
// This should have already been handled.
|
|
|
|
//
|
|
|
|
// Mov $18, $31
|
|
|
|
|
2015-08-11 16:56:25 +08:00
|
|
|
EmitInstrRegRegReg(*STI, Mips::OR, Mips::S2, Mips::RA, Mips::ZERO);
|
2014-02-15 03:16:39 +08:00
|
|
|
|
2015-02-21 16:48:22 +08:00
|
|
|
EmitSwapFPIntParams(*STI, Signature->ParamSig, LE, true);
|
2014-02-15 03:16:39 +08:00
|
|
|
|
|
|
|
// Jal xxxx
|
|
|
|
//
|
2015-02-21 16:48:22 +08:00
|
|
|
EmitJal(*STI, MSymbol);
|
2014-02-15 03:16:39 +08:00
|
|
|
|
|
|
|
// fix return values
|
2015-02-21 16:48:22 +08:00
|
|
|
EmitSwapFPIntRetval(*STI, Signature->RetSig, LE);
|
2014-02-15 03:16:39 +08:00
|
|
|
//
|
|
|
|
// do the return
|
|
|
|
// if (Signature->RetSig == NoFPRet)
|
|
|
|
// llvm_unreachable("should not be any stubs here with no return value");
|
|
|
|
// else
|
2015-02-21 16:48:22 +08:00
|
|
|
EmitInstrReg(*STI, Mips::JR, Mips::S2);
|
2014-02-15 03:16:39 +08:00
|
|
|
|
2015-05-19 02:43:14 +08:00
|
|
|
MCSymbol *Tmp = OutContext.createTempSymbol();
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->EmitLabel(Tmp);
|
2015-05-30 09:25:56 +08:00
|
|
|
const MCSymbolRefExpr *E = MCSymbolRefExpr::create(Stub, OutContext);
|
|
|
|
const MCSymbolRefExpr *T = MCSymbolRefExpr::create(Tmp, OutContext);
|
|
|
|
const MCExpr *T_min_E = MCBinaryExpr::createSub(T, E, OutContext);
|
2015-06-02 08:25:12 +08:00
|
|
|
OutStreamer->emitELFSize(Stub, T_min_E);
|
2014-02-15 03:16:39 +08:00
|
|
|
TS.emitDirectiveEnd(x);
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->PopSection();
|
2014-02-15 03:16:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void MipsAsmPrinter::EmitEndOfAsmFile(Module &M) {
|
|
|
|
// Emit needed stubs
|
|
|
|
//
|
|
|
|
for (std::map<
|
|
|
|
const char *,
|
2017-08-04 06:12:30 +08:00
|
|
|
const Mips16HardFloatInfo::FuncSignature *>::const_iterator
|
2014-02-15 03:16:39 +08:00
|
|
|
it = StubsNeeded.begin();
|
|
|
|
it != StubsNeeded.end(); ++it) {
|
|
|
|
const char *Symbol = it->first;
|
2017-08-04 06:12:30 +08:00
|
|
|
const Mips16HardFloatInfo::FuncSignature *Signature = it->second;
|
2014-02-15 03:16:39 +08:00
|
|
|
EmitFPCallStub(Symbol, Signature);
|
|
|
|
}
|
2014-01-27 09:33:33 +08:00
|
|
|
// return to the text section
|
2015-04-25 03:11:51 +08:00
|
|
|
OutStreamer->SwitchSection(OutContext.getObjectFileInfo()->getTextSection());
|
2013-01-19 05:20:38 +08:00
|
|
|
}
|
|
|
|
|
2017-02-15 18:48:11 +08:00
|
|
|
void MipsAsmPrinter::EmitSled(const MachineInstr &MI, SledKind Kind) {
|
|
|
|
const uint8_t NoopsInSledCount = Subtarget->isGP64bit() ? 15 : 11;
|
|
|
|
// For mips32 we want to emit the following pattern:
|
|
|
|
//
|
|
|
|
// .Lxray_sled_N:
|
|
|
|
// ALIGN
|
|
|
|
// B .tmpN
|
|
|
|
// 11 NOP instructions (44 bytes)
|
2018-07-31 03:41:25 +08:00
|
|
|
// ADDIU T9, T9, 52
|
2017-02-15 18:48:11 +08:00
|
|
|
// .tmpN
|
|
|
|
//
|
|
|
|
// We need the 44 bytes (11 instructions) because at runtime, we'd
|
|
|
|
// be patching over the full 48 bytes (12 instructions) with the following
|
|
|
|
// pattern:
|
|
|
|
//
|
2017-10-18 21:31:28 +08:00
|
|
|
// ADDIU SP, SP, -8
|
2017-02-15 18:48:11 +08:00
|
|
|
// NOP
|
2017-10-18 21:31:28 +08:00
|
|
|
// SW RA, 4(SP)
|
2017-02-15 18:48:11 +08:00
|
|
|
// SW T9, 0(SP)
|
|
|
|
// LUI T9, %hi(__xray_FunctionEntry/Exit)
|
|
|
|
// ORI T9, T9, %lo(__xray_FunctionEntry/Exit)
|
|
|
|
// LUI T0, %hi(function_id)
|
2017-10-18 21:31:28 +08:00
|
|
|
// JALR T9
|
|
|
|
// ORI T0, T0, %lo(function_id)
|
|
|
|
// LW T9, 0(SP)
|
2017-02-15 18:48:11 +08:00
|
|
|
// LW RA, 4(SP)
|
|
|
|
// ADDIU SP, SP, 8
|
|
|
|
//
|
|
|
|
// We add 52 bytes to t9 because we want to adjust the function pointer to
|
|
|
|
// the actual start of function i.e. the address just after the noop sled.
|
|
|
|
// We do this because gp displacement relocation is emitted at the start of
|
|
|
|
// of the function i.e after the nop sled and to correctly calculate the
|
|
|
|
// global offset table address, t9 must hold the address of the instruction
|
|
|
|
// containing the gp displacement relocation.
|
|
|
|
// FIXME: Is this correct for the static relocation model?
|
|
|
|
//
|
|
|
|
// For mips64 we want to emit the following pattern:
|
|
|
|
//
|
|
|
|
// .Lxray_sled_N:
|
|
|
|
// ALIGN
|
|
|
|
// B .tmpN
|
|
|
|
// 15 NOP instructions (60 bytes)
|
|
|
|
// .tmpN
|
|
|
|
//
|
|
|
|
// We need the 60 bytes (15 instructions) because at runtime, we'd
|
|
|
|
// be patching over the full 64 bytes (16 instructions) with the following
|
|
|
|
// pattern:
|
|
|
|
//
|
|
|
|
// DADDIU SP, SP, -16
|
|
|
|
// NOP
|
|
|
|
// SD RA, 8(SP)
|
|
|
|
// SD T9, 0(SP)
|
|
|
|
// LUI T9, %highest(__xray_FunctionEntry/Exit)
|
|
|
|
// ORI T9, T9, %higher(__xray_FunctionEntry/Exit)
|
|
|
|
// DSLL T9, T9, 16
|
|
|
|
// ORI T9, T9, %hi(__xray_FunctionEntry/Exit)
|
|
|
|
// DSLL T9, T9, 16
|
|
|
|
// ORI T9, T9, %lo(__xray_FunctionEntry/Exit)
|
|
|
|
// LUI T0, %hi(function_id)
|
|
|
|
// JALR T9
|
|
|
|
// ADDIU T0, T0, %lo(function_id)
|
|
|
|
// LD T9, 0(SP)
|
|
|
|
// LD RA, 8(SP)
|
|
|
|
// DADDIU SP, SP, 16
|
|
|
|
//
|
|
|
|
OutStreamer->EmitCodeAlignment(4);
|
|
|
|
auto CurSled = OutContext.createTempSymbol("xray_sled_", true);
|
|
|
|
OutStreamer->EmitLabel(CurSled);
|
|
|
|
auto Target = OutContext.createTempSymbol();
|
|
|
|
|
|
|
|
// Emit "B .tmpN" instruction, which jumps over the nop sled to the actual
|
|
|
|
// start of function
|
|
|
|
const MCExpr *TargetExpr = MCSymbolRefExpr::create(
|
|
|
|
Target, MCSymbolRefExpr::VariantKind::VK_None, OutContext);
|
|
|
|
EmitToStreamer(*OutStreamer, MCInstBuilder(Mips::BEQ)
|
|
|
|
.addReg(Mips::ZERO)
|
|
|
|
.addReg(Mips::ZERO)
|
|
|
|
.addExpr(TargetExpr));
|
|
|
|
|
|
|
|
for (int8_t I = 0; I < NoopsInSledCount; I++)
|
|
|
|
EmitToStreamer(*OutStreamer, MCInstBuilder(Mips::SLL)
|
|
|
|
.addReg(Mips::ZERO)
|
|
|
|
.addReg(Mips::ZERO)
|
|
|
|
.addImm(0));
|
|
|
|
|
|
|
|
OutStreamer->EmitLabel(Target);
|
|
|
|
|
|
|
|
if (!Subtarget->isGP64bit()) {
|
|
|
|
EmitToStreamer(*OutStreamer,
|
|
|
|
MCInstBuilder(Mips::ADDiu)
|
|
|
|
.addReg(Mips::T9)
|
|
|
|
.addReg(Mips::T9)
|
|
|
|
.addImm(0x34));
|
|
|
|
}
|
|
|
|
|
|
|
|
recordSled(CurSled, MI, Kind);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MipsAsmPrinter::LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI) {
|
|
|
|
EmitSled(MI, SledKind::FUNCTION_ENTER);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MipsAsmPrinter::LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI) {
|
|
|
|
EmitSled(MI, SledKind::FUNCTION_EXIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MipsAsmPrinter::LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI) {
|
|
|
|
EmitSled(MI, SledKind::TAIL_CALL);
|
|
|
|
}
|
|
|
|
|
2011-07-01 09:04:43 +08:00
|
|
|
void MipsAsmPrinter::PrintDebugValueComment(const MachineInstr *MI,
|
|
|
|
raw_ostream &OS) {
|
|
|
|
// TODO: implement
|
|
|
|
}
|
|
|
|
|
2017-01-21 01:53:30 +08:00
|
|
|
// Emit .dtprelword or .dtpreldword directive
|
|
|
|
// and value for debug thread local expression.
|
2017-02-09 03:03:46 +08:00
|
|
|
void MipsAsmPrinter::EmitDebugThreadLocal(const MCExpr *Value,
|
2017-01-21 01:53:30 +08:00
|
|
|
unsigned Size) const {
|
|
|
|
switch (Size) {
|
|
|
|
case 4:
|
|
|
|
OutStreamer->EmitDTPRel32Value(Value);
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
OutStreamer->EmitDTPRel64Value(Value);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
llvm_unreachable("Unexpected size of expression value.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-28 18:00:38 +08:00
|
|
|
// Align all targets of indirect branches on bundle size. Used only if target
|
|
|
|
// is NaCl.
|
|
|
|
void MipsAsmPrinter::NaClAlignIndirectJumpTargets(MachineFunction &MF) {
|
|
|
|
// Align all blocks that are jumped to through jump table.
|
|
|
|
if (MachineJumpTableInfo *JtInfo = MF.getJumpTableInfo()) {
|
|
|
|
const std::vector<MachineJumpTableEntry> &JT = JtInfo->getJumpTables();
|
|
|
|
for (unsigned I = 0; I < JT.size(); ++I) {
|
|
|
|
const std::vector<MachineBasicBlock*> &MBBs = JT[I].MBBs;
|
|
|
|
|
|
|
|
for (unsigned J = 0; J < MBBs.size(); ++J)
|
|
|
|
MBBs[J]->setAlignment(MIPS_NACL_BUNDLE_ALIGN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If basic block address is taken, block can be target of indirect branch.
|
2016-04-16 04:43:17 +08:00
|
|
|
for (auto &MBB : MF) {
|
|
|
|
if (MBB.hasAddressTaken())
|
|
|
|
MBB.setAlignment(MIPS_NACL_BUNDLE_ALIGN);
|
2014-02-28 18:00:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-30 23:06:25 +08:00
|
|
|
bool MipsAsmPrinter::isLongBranchPseudo(int Opcode) const {
|
|
|
|
return (Opcode == Mips::LONG_BRANCH_LUi
|
|
|
|
|| Opcode == Mips::LONG_BRANCH_ADDiu
|
|
|
|
|| Opcode == Mips::LONG_BRANCH_DADDiu);
|
|
|
|
}
|
|
|
|
|
2009-06-24 07:59:40 +08:00
|
|
|
// Force static initialization.
|
2011-03-05 01:51:39 +08:00
|
|
|
extern "C" void LLVMInitializeMipsAsmPrinter() {
|
2016-10-10 07:00:34 +08:00
|
|
|
RegisterAsmPrinter<MipsAsmPrinter> X(getTheMipsTarget());
|
|
|
|
RegisterAsmPrinter<MipsAsmPrinter> Y(getTheMipselTarget());
|
|
|
|
RegisterAsmPrinter<MipsAsmPrinter> A(getTheMips64Target());
|
|
|
|
RegisterAsmPrinter<MipsAsmPrinter> B(getTheMips64elTarget());
|
2009-07-16 04:24:03 +08:00
|
|
|
}
|