diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index dc7dc33113cbcd..e3781564b8a3e5 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -3487,6 +3487,8 @@ def mxgot : Flag<["-"], "mxgot">, Group; def mno_xgot : Flag<["-"], "mno-xgot">, Group; def mjump_table_opt : Flag<["-"], "mjump-table-opt">, Group; def mno_jump_table_opt : Flag<["-"], "mno-jump-table-opt">, Group; +def mfix_nmips_hw110880 : Flag<["-"], "mfix-nmips-hw110880">, Group; +def mno_fix_nmips_hw110880 : Flag<["-"], "mno-fix-nmips-hw110880">, Group; def mldc1_sdc1 : Flag<["-"], "mldc1-sdc1">, Group; def mno_ldc1_sdc1 : Flag<["-"], "mno-ldc1-sdc1">, Group; def mcheck_zero_division : Flag<["-"], "mcheck-zero-division">, diff --git a/clang/lib/Driver/ToolChains/Arch/Mips.cpp b/clang/lib/Driver/ToolChains/Arch/Mips.cpp index d9b0d37d7f77b1..5be7226575047c 100644 --- a/clang/lib/Driver/ToolChains/Arch/Mips.cpp +++ b/clang/lib/Driver/ToolChains/Arch/Mips.cpp @@ -300,6 +300,14 @@ void mips::getMIPSTargetFeatures(const Driver &D, const llvm::Triple &Triple, Features.push_back("-disable-jump-table-opt"); } + if (Arg *A = Args.getLastArg(options::OPT_mfix_nmips_hw110880, + options::OPT_mno_fix_nmips_hw110880)) { + if (A->getOption().matches(options::OPT_mfix_nmips_hw110880)) + Features.push_back("+fix-hw110880"); + else + Features.push_back("-fix-hw110880"); + } + mips::FloatABI FloatABI = mips::getMipsFloatABI(D, Args, Triple); if (FloatABI == mips::FloatABI::Soft) { // FIXME: Note, this is a hack. We need to pass the selected float diff --git a/clang/lib/Driver/ToolChains/NanoMips.cpp b/clang/lib/Driver/ToolChains/NanoMips.cpp index a1d46da1d4c0dd..8c7a0385127bec 100644 --- a/clang/lib/Driver/ToolChains/NanoMips.cpp +++ b/clang/lib/Driver/ToolChains/NanoMips.cpp @@ -128,6 +128,11 @@ void NanoMipsLinker::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("--relax"); } + if (Args.hasFlag(options::OPT_mfix_nmips_hw110880, + options::OPT_mno_fix_nmips_hw110880, false)) { + CmdArgs.push_back("--fix-nmips-hw110880"); + } + Args.AddAllArgs(CmdArgs, options::OPT_L); Args.AddAllArgs(CmdArgs, options::OPT_u); diff --git a/llvm/lib/Target/Mips/Mips.td b/llvm/lib/Target/Mips/Mips.td index 519feb84cc340f..141ef3652d390a 100644 --- a/llvm/lib/Target/Mips/Mips.td +++ b/llvm/lib/Target/Mips/Mips.td @@ -226,6 +226,8 @@ def FeatureUseIndirectJumpsHazard : SubtargetFeature<"use-indirect-jump-hazard", "true", "Use indirect jump" " guards to prevent certain speculation based attacks">; +def FeatureFixImm48 : SubtargetFeature<"fix-hw110880", "HasFixHw110880", "true", "Avoid problematic instructions for hw110880 issue">; + //===----------------------------------------------------------------------===// // Register File, Calling Conv, Instruction Descriptions //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/Mips/MipsAsmPrinter.cpp b/llvm/lib/Target/Mips/MipsAsmPrinter.cpp index 906fbab35238f1..3e8732b4bac990 100644 --- a/llvm/lib/Target/Mips/MipsAsmPrinter.cpp +++ b/llvm/lib/Target/Mips/MipsAsmPrinter.cpp @@ -72,6 +72,11 @@ using namespace llvm; extern cl::opt EmitJalrReloc; +cl::opt +FixHw110880("nmips-fix-hw110880", cl::Hidden, + cl::desc("nanoMIPS: Fix problematic instructions to avoid hw110880 issue"), + cl::init(true)); + void MipsAsmPrinter::emitJumpTableInfo() { if (!Subtarget->hasNanoMips() || Subtarget->useAbsoluteJumpTables() ) { AsmPrinter::emitJumpTableInfo(); @@ -249,6 +254,56 @@ void MipsAsmPrinter::emitJumpTableDest(MCStreamer &OutStreamer, EmitToStreamer(OutStreamer, LoadI); } +bool MipsAsmPrinter::tryEmitHw110880Fix(MCStreamer &OutStreamer, + const MachineInstr *MI) { + if (!FixHw110880) + return false; + + if (!Subtarget->hasFixHw110880()) + return false; + + if (MI->getOpcode() != Mips::Li_NM && + MI->getOpcode() != Mips::ADDIU48_NM) + return false; + + int LastOp = MI->getNumOperands() - 1; + auto Last = MI->getOperand(LastOp); + + if (!Last.isImm()) + return false; + + int64_t Value = Last.getImm(); + + auto ImmValueNeedsHw110880Fix = [MI](int64_t Value) -> bool { + // Check if we are dealing with 48-bit LI + if ((MI->getOpcode() == Mips::Li_NM) && + (isUInt<16>(Value) || isInt<9>(Value) || ((Value & 0xfffu) == 0u))) + return false; + + return (((Value & 0x50016400) == 0x50012400) && + (((Value >> 24) & 0x7) == 0x1 || ((Value >> 24) & 0x2) == 0x2)); + }; + + if (!ImmValueNeedsHw110880Fix(Value)) + return false; + + MCSymbol *AbsZero = OutContext.getOrCreateSymbol( + Twine(getDataLayout().getPrivateGlobalPrefix()) + + Twine("zero")); + + const MCExpr *ZeroRef = MCSymbolRefExpr::create(AbsZero, OutContext); + const MCExpr *ValueExpr = MCBinaryExpr::createAdd( + ZeroRef, MCConstantExpr::create(Value, OutContext, true, 4u), OutContext); + + MCInst Load; + Load.setOpcode(MI->getOpcode()); + for (int i = 0; i < LastOp; i++) + Load.addOperand(MCOperand::createReg(MI->getOperand(i).getReg())); + Load.addOperand(MCOperand::createExpr(ValueExpr)); + EmitToStreamer(OutStreamer, Load); + return true; +} + // Each table starts with the following directive: // // .jumptable esize, nsize [, unsigned] @@ -417,6 +472,12 @@ void MipsAsmPrinter::emitInstruction(const MachineInstr *MI) { continue; } + if (Subtarget->hasNanoMips() && + tryEmitHw110880Fix(*OutStreamer, &*I)) { + continue; + } + + if (Subtarget->hasNanoMips() && (I->getOpcode() == Mips::LoadJumpTableOffset)) { emitJumpTableDest(*OutStreamer, &*I); @@ -1294,6 +1355,20 @@ void MipsAsmPrinter::EmitFPCallStub( } void MipsAsmPrinter::emitEndOfAsmFile(Module &M) { + if (Subtarget->hasNanoMips() && FixHw110880) { + SmallString<8> Name; + auto Symbols = OutContext.getSymbols(); + MCSymbol *AbsZero = Symbols.lookup( + (Twine(getDataLayout().getPrivateGlobalPrefix()) + Twine("zero")) + .toStringRef(Name)); + + if (AbsZero) { + assert(!AbsZero->isDefined()); + // Force assembler to use relocations via weak symbol + OutStreamer->emitSymbolAttribute(AbsZero, MCSymbolAttr::MCSA_Weak); + OutStreamer->emitAssignment(AbsZero, MCConstantExpr::create(0u, OutContext)); + } + } // Emit needed stubs // for (std::map< diff --git a/llvm/lib/Target/Mips/MipsAsmPrinter.h b/llvm/lib/Target/Mips/MipsAsmPrinter.h index d55c5544397458..3a3fbde7f57a20 100644 --- a/llvm/lib/Target/Mips/MipsAsmPrinter.h +++ b/llvm/lib/Target/Mips/MipsAsmPrinter.h @@ -80,6 +80,8 @@ class LLVM_LIBRARY_VISIBILITY MipsAsmPrinter : public AsmPrinter { void emitJumpTableDest(MCStreamer &OutStreamer, const MachineInstr *MI); + bool tryEmitHw110880Fix(MCStreamer &OutStreamer, const MachineInstr *MI); + // Emit brsc instruction followed by a label that will be used while creating // offset expressions in jump table entries. void emitBrsc(MCStreamer &OutStreamer, const MachineInstr *MI); diff --git a/llvm/lib/Target/Mips/MipsSubtarget.h b/llvm/lib/Target/Mips/MipsSubtarget.h index b945e3eec13323..061f53d9d148a9 100644 --- a/llvm/lib/Target/Mips/MipsSubtarget.h +++ b/llvm/lib/Target/Mips/MipsSubtarget.h @@ -207,6 +207,8 @@ class MipsSubtarget : public MipsGenSubtargetInfo { // Use linker relaxation bool UseLinkerRelax = true; + bool HasFixHw110880 = false; + /// The minimum alignment known to hold of the stack frame on /// entry to the function and which must be maintained by every function. Align stackAlignment; @@ -355,6 +357,8 @@ class MipsSubtarget : public MipsGenSubtargetInfo { bool useLinkerRelax() const { return UseLinkerRelax; } + bool hasFixHw110880() const { return HasFixHw110880; } + bool enableLongBranchPass() const { return hasStandardEncoding() || inMicroMipsMode() || allowMixed16_32(); } diff --git a/llvm/test/CodeGen/Mips/nanomips/addiu.ll b/llvm/test/CodeGen/Mips/nanomips/addiu.ll index 1e570992001812..80b6dfaff344f6 100644 --- a/llvm/test/CodeGen/Mips/nanomips/addiu.ll +++ b/llvm/test/CodeGen/Mips/nanomips/addiu.ll @@ -1,13 +1,13 @@ -; RUN: llc -mtriple=nanomips -asm-show-inst -verify-machineinstrs < %s | FileCheck %s - +; RUN: llc -mtriple=nanomips -asm-show-inst -verify-machineinstrs < %s | FileCheck --check-prefix=CHECK-ALL --check-prefix=CHECK %s +; RUN: llc -mtriple=nanomips -asm-show-inst -mattr=+fix-hw110880 -verify-machineinstrs < %s | FileCheck --check-prefix=CHECK-ALL --check-prefix=CHECK-FIX %s define i32 @test_addiu0(i32 %a) { -; CHECK: addiu $a0, $a0, 1 +; CHECK-ALL: addiu $a0, $a0, 1 %added = add i32 %a, 1 ret i32 %added } define i32 @test_addiu1(i32 %a) { -; CHECK: addiu $a0, $a0, 65535 +; CHECK-ALL: addiu $a0, $a0, 65535 %added = add i32 %a, 65535 ret i32 %added } @@ -19,7 +19,7 @@ define i32 @test_addiu2(i32 %a) { } define i32 @test_addiu3(i32 %a) { -; CHECK: addiu $a0, $a0, -2048 +; CHECK-ALL: addiu $a0, $a0, -2048 %added = add i32 %a, -2048 ret i32 %added } @@ -31,26 +31,33 @@ define i32 @test_addiu4(i32 %a) { } define i32 @test_addiu5(i32 %a) { -; CHECK: addiu[48] $a0, $a0, 2147483647 +; CHECK-ALL: addiu[48] $a0, $a0, 2147483647 %added = add i32 %a, 2147483647 ret i32 %added } define i32 @test_addiu6(i32 %a) { -; CHECK: addiu[48] $a0, $a0, -2147483648 +; CHECK-ALL: addiu[48] $a0, $a0, -2147483648 %added = add i32 %a, -2147483648 ret i32 %added } define i32 @test_addiu7(i32 %a, i32 %b) { -; CHECK: move $a0, $a1 -; CHECK: addiu[48] $a0, $a0, -2049 +; CHECK-ALL: move $a0, $a1 +; CHECK-ALL: addiu[48] $a0, $a0, -2049 %added = add i32 %b, -2049 ret i32 %added } define i32 @test_addiu8(i32 %a) { -; CHECK: addiu[48] $a0, $a0, -2147483648 +; CHECK-ALL: addiu[48] $a0, $a0, -2147483648 %added = add i32 %a, 2147483648 ret i32 %added } + +define i32 @test_addiu9(i32 %a) { +; CHECK: addiu[48] $a0, $a0, 1375806708 +; CHECK-FIX: addiu[48] $a0, $a0, .Lzero+0x520124f4 + %added = add i32 %a, 1375806708 + ret i32 %added +} \ No newline at end of file diff --git a/llvm/test/CodeGen/Mips/nanomips/li.ll b/llvm/test/CodeGen/Mips/nanomips/li.ll index 7b91c9d45ce246..5ce256624c5765 100644 --- a/llvm/test/CodeGen/Mips/nanomips/li.ll +++ b/llvm/test/CodeGen/Mips/nanomips/li.ll @@ -1,25 +1,43 @@ -; RUN: llc -mtriple=nanomips -asm-show-inst -verify-machineinstrs < %s | FileCheck %s +; RUN: llc -mtriple=nanomips -asm-show-inst -verify-machineinstrs < %s | FileCheck --check-prefix=CHECK-ALL --check-prefix=CHECK %s +; RUN: llc -mtriple=nanomips -asm-show-inst -mattr=+fix-hw110880 -verify-machineinstrs < %s | FileCheck --check-prefix=CHECK-ALL --check-prefix=CHECK-FIX %s define i32 @foo0() nounwind readnone { ; CHECK-LABEL: foo0 entry: -; CHECK: li $a0, 12345 -; CHECK: Li_NM +; CHECK-ALL: li $a0, 12345 +; CHECK-ALL: Li_NM ret i32 12345 } define i32 @foo1() nounwind readnone { ; CHECK-LABEL: foo1 entry: -; CHECK: li $a0, -2147483648 -; CHECK: Li_NM +; CHECK-ALL: li $a0, -2147483648 +; CHECK-ALL: Li_NM ret i32 -2147483648 } define i32 @foo2() nounwind readnone { ; CHECK-LABEL: foo2 entry: -; CHECK: li $a0, 2147483647 -; CHECK: Li_NM +; CHECK-ALL: li $a0, 2147483647 +; CHECK-ALL: Li_NM ret i32 2147483647 } + +define i32 @foo3() nounwind readnone { +; CHECK-LABEL: foo3 +entry: +; CHECK-ALL: li $a0, 2147479552 +; CHECK-ALL: Li_NM + ret i32 2147479552 +} + +define i32 @foo4() nounwind readnone { +; CHECK-LABEL: foo4 +entry: +; CHECK: li $a0, 1375806708 +; CHECK-FIX: li $a0, .Lzero+0x520124f4 +; CHECK-ALL: Li_NM + ret i32 1375806708 +}