Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -3146,6 +3146,10 @@ HelpText<"Enable using library calls for save and restore">; def mno_save_restore : Flag<["-"], "mno-save-restore">, Group, HelpText<"Disable using library calls for save and restore">; +def mno_div : Flag<["-"], "mno-div">, Group, + HelpText<"Disable hardware integral division instructions in M extension">; +def mdiv : Flag<["-"], "mdiv">, Group, + HelpText<"Enable hardware integral division instructions in M extension">; def mcmodel_EQ_medlow : Flag<["-"], "mcmodel=medlow">, Group, Flags<[CC1Option]>, Alias, AliasArgs<["small"]>, HelpText<"Equivalent to -mcmodel=small, compatible with RISC-V gcc.">; Index: clang/lib/Basic/Targets/RISCV.h =================================================================== --- clang/lib/Basic/Targets/RISCV.h +++ clang/lib/Basic/Targets/RISCV.h @@ -46,6 +46,7 @@ bool HasZfh = false; bool HasZvamo = false; bool HasZvlsseg = false; + bool DisableHardwareIntDiv = false; static const Builtin::Info BuiltinInfo[]; Index: clang/lib/Basic/Targets/RISCV.cpp =================================================================== --- clang/lib/Basic/Targets/RISCV.cpp +++ clang/lib/Basic/Targets/RISCV.cpp @@ -144,8 +144,10 @@ if (HasM) { Builder.defineMacro("__riscv_m", "2000000"); Builder.defineMacro("__riscv_mul"); - Builder.defineMacro("__riscv_div"); - Builder.defineMacro("__riscv_muldiv"); + if (!DisableHardwareIntDiv) { + Builder.defineMacro("__riscv_div"); + Builder.defineMacro("__riscv_muldiv"); + } } if (HasA) { @@ -278,6 +280,7 @@ .Case("experimental-zfh", HasZfh) .Case("experimental-zvamo", HasZvamo) .Case("experimental-zvlsseg", HasZvlsseg) + .Case("no-div", DisableHardwareIntDiv) .Default(false); } @@ -327,6 +330,8 @@ HasZvamo = true; else if (Feature == "+experimental-zvlsseg") HasZvlsseg = true; + else if (Feature == "+no-div") + DisableHardwareIntDiv = true; } return true; Index: clang/lib/Driver/ToolChains/Arch/RISCV.cpp =================================================================== --- clang/lib/Driver/ToolChains/Arch/RISCV.cpp +++ clang/lib/Driver/ToolChains/Arch/RISCV.cpp @@ -570,6 +570,12 @@ // Now add any that the user explicitly requested on the command line, // which may override the defaults. handleTargetFeaturesGroup(Args, Features, options::OPT_m_riscv_Features_Group); + for (auto &Feature : Features) + if (Feature == "-div") { + Feature = "+no-div"; + } else if (Feature == "+div") { + Feature = "-no-div"; + } } StringRef riscv::getRISCVABI(const ArgList &Args, const llvm::Triple &Triple) { Index: clang/test/Driver/riscv-no-div.c =================================================================== --- /dev/null +++ clang/test/Driver/riscv-no-div.c @@ -0,0 +1,37 @@ +// RUN: %clang -target riscv32-unknown-elf %s -mno-div -S -o - 2>&1 \ +// RUN: | not FileCheck -check-prefix=CHECK-NOERROR %s + +// RUN: %clang -target riscv64-unknown-elf %s -mno-div -S -o - 2>&1 \ +// RUN: | not FileCheck -check-prefix=CHECK-NOERROR %s + +// RUN: %clang -target riscv32-unknown-elf %s -mno-div -S -o - 2>&1 \ +// RUN: | not FileCheck -check-prefix=CHECK-DIV %s + +// RUN: %clang -target riscv64-unknown-elf %s -mno-div -S -o - 2>&1 \ +// RUN: | not FileCheck -check-prefix=CHECK-DIV %s + +// RUN: %clang -target riscv32-unknown-elf %s -mno-div -S -o - 2>&1 \ +// RUN: | not FileCheck -check-prefix=CHECK-REM %s + +// RUN: %clang -target riscv64-unknown-elf %s -mno-div -S -o - 2>&1 \ +// RUN: | not FileCheck -check-prefix=CHECK-REM %s + +// RUN: %clang -target riscv32-unknown-elf %s -mno-div -S -o - 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-MUL %s + +// RUN: %clang -target riscv64-unknown-elf %s -mno-div -S -o - 2>&1 \ +// RUN: | FileCheck -check-prefix=CHECK-MUL %s + +// RUN: %clang -target riscv32-unknown-elf %s -S -o - 2>&1 \ +// RUN: | FileCheck -check-prefixes=CHECK-MUL,CHECK-DIV,CHECK-REM %s + +// RUN: %clang -target riscv64-unknown-elf %s -S -o - 2>&1 \ +// RUN: | FileCheck -check-prefixes=CHECK-MUL,CHECK-DIV,CHECK-REM %s + +int foo(int x, int y) { + // CHECK-NOERROR: is not a recognized feature for this target + // CHECK-DIV: div{{w?}} a{{[0-9]}}, a{{[0-9]}}, a{{[0-9]}} + // CHECK-REM: rem{{w?}} a{{[0-9]}}, a{{[0-9]}}, a{{[0-9]}} + // CHECK-MUL: mul{{w?}} a{{[0-9]}}, a{{[0-9]}}, a{{[0-9]}} + return (x/y)*(x%y); +} Index: llvm/lib/Target/RISCV/RISCV.td =================================================================== --- llvm/lib/Target/RISCV/RISCV.td +++ llvm/lib/Target/RISCV/RISCV.td @@ -19,6 +19,13 @@ AssemblerPredicate<(all_of FeatureStdExtM), "'M' (Integer Multiplication and Division)">; +def FeatureNoIntDiv : SubtargetFeature<"no-div", "DisableHardwareIntDiv", "true", + "Disable hardware integral division" + "instructions from M Extension.">; +def HasIntDiv : Predicate<"!Subtarget->disableHardwareIntDiv()">, + AssemblerPredicate<(all_of (not FeatureNoIntDiv)), + "Hardware integral division not disabled">; + def FeatureStdExtA : SubtargetFeature<"a", "HasStdExtA", "true", "'A' (Atomic Instructions)">; Index: llvm/lib/Target/RISCV/RISCVISelLowering.cpp =================================================================== --- llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -205,15 +205,22 @@ setOperationAction(ISD::MUL, XLenVT, Expand); setOperationAction(ISD::MULHS, XLenVT, Expand); setOperationAction(ISD::MULHU, XLenVT, Expand); + } else { + if (Subtarget.is64Bit()) { + setOperationAction(ISD::MUL, MVT::i32, Custom); + setOperationAction(ISD::MUL, MVT::i128, Custom); + } else { + setOperationAction(ISD::MUL, MVT::i64, Custom); + } + } + + if (!Subtarget.hasStdExtM() || Subtarget.disableHardwareIntDiv()) { setOperationAction(ISD::SDIV, XLenVT, Expand); setOperationAction(ISD::UDIV, XLenVT, Expand); setOperationAction(ISD::SREM, XLenVT, Expand); setOperationAction(ISD::UREM, XLenVT, Expand); } else { if (Subtarget.is64Bit()) { - setOperationAction(ISD::MUL, MVT::i32, Custom); - setOperationAction(ISD::MUL, MVT::i128, Custom); - setOperationAction(ISD::SDIV, MVT::i8, Custom); setOperationAction(ISD::UDIV, MVT::i8, Custom); setOperationAction(ISD::UREM, MVT::i8, Custom); @@ -223,8 +230,6 @@ setOperationAction(ISD::SDIV, MVT::i32, Custom); setOperationAction(ISD::UDIV, MVT::i32, Custom); setOperationAction(ISD::UREM, MVT::i32, Custom); - } else { - setOperationAction(ISD::MUL, MVT::i64, Custom); } } Index: llvm/lib/Target/RISCV/RISCVInstrInfoM.td =================================================================== --- llvm/lib/Target/RISCV/RISCVInstrInfoM.td +++ llvm/lib/Target/RISCV/RISCVInstrInfoM.td @@ -33,6 +33,9 @@ Sched<[WriteIMul, ReadIMul, ReadIMul]>; def MULHU : ALU_rr<0b0000001, 0b011, "mulhu">, Sched<[WriteIMul, ReadIMul, ReadIMul]>; +} // Predicates = [HasStdExtM] + +let Predicates = [HasStdExtM, HasIntDiv] in { def DIV : ALU_rr<0b0000001, 0b100, "div">, Sched<[WriteIDiv, ReadIDiv, ReadIDiv]>; def DIVU : ALU_rr<0b0000001, 0b101, "divu">, @@ -41,11 +44,14 @@ Sched<[WriteIDiv, ReadIDiv, ReadIDiv]>; def REMU : ALU_rr<0b0000001, 0b111, "remu">, Sched<[WriteIDiv, ReadIDiv, ReadIDiv]>; -} // Predicates = [HasStdExtM] +} // Predicates = [HasStdExtM, HasIntDiv] let Predicates = [HasStdExtM, IsRV64] in { def MULW : ALUW_rr<0b0000001, 0b000, "mulw">, Sched<[WriteIMul32, ReadIMul32, ReadIMul32]>; +} // Predicates = [HasStdExtM, IsRV64] + +let Predicates = [HasStdExtM, IsRV64, HasIntDiv] in { def DIVW : ALUW_rr<0b0000001, 0b100, "divw">, Sched<[WriteIDiv32, ReadIDiv32, ReadIDiv32]>; def DIVUW : ALUW_rr<0b0000001, 0b101, "divuw">, @@ -54,7 +60,7 @@ Sched<[WriteIDiv32, ReadIDiv32, ReadIDiv32]>; def REMUW : ALUW_rr<0b0000001, 0b111, "remuw">, Sched<[WriteIDiv32, ReadIDiv32, ReadIDiv32]>; -} // Predicates = [HasStdExtM, IsRV64] +} // Predicates = [HasStdExtM, IsRV64, HasIntDiv] //===----------------------------------------------------------------------===// // Pseudo-instructions and codegen patterns @@ -65,16 +71,21 @@ def : PatGprGpr; def : PatGprGpr; def : PatGprGpr; +} // Predicates = [HasStdExtM] + +let Predicates = [HasStdExtM, HasIntDiv] in { def : PatGprGpr; def : PatGprGpr; def : PatGprGpr; def : PatGprGpr; -} // Predicates = [HasStdExtM] +} // Predicates = [HasStdExtM, HasIntDiv] let Predicates = [HasStdExtM, IsRV64] in { def : Pat<(sext_inreg (mul GPR:$rs1, GPR:$rs2), i32), (MULW GPR:$rs1, GPR:$rs2)>; +} // Predicates = [HasStdExtM, IsRV64] +let Predicates = [HasStdExtM, IsRV64, HasIntDiv] in { def : PatGprGpr; def : PatGprGpr; def : PatGprGpr; @@ -94,7 +105,7 @@ // produce a result where res[63:32]=0 and res[31]=1. def : Pat<(srem (sexti32 (i64 GPR:$rs1)), (sexti32 (i64 GPR:$rs2))), (REMW GPR:$rs1, GPR:$rs2)>; -} // Predicates = [HasStdExtM, IsRV64] +} // Predicates = [HasStdExtM, IsRV64, HasIntDiv] let Predicates = [HasStdExtM, IsRV64, NotHasStdExtZba] in { // Special case for calculating the full 64-bit product of a 32x32 unsigned Index: llvm/lib/Target/RISCV/RISCVSubtarget.h =================================================================== --- llvm/lib/Target/RISCV/RISCVSubtarget.h +++ llvm/lib/Target/RISCV/RISCVSubtarget.h @@ -60,6 +60,7 @@ bool EnableLinkerRelax = false; bool EnableRVCHintInstrs = true; bool EnableSaveRestore = false; + bool DisableHardwareIntDiv = false; unsigned XLen = 32; MVT XLenVT = MVT::i32; RISCVABI::ABI TargetABI = RISCVABI::ABI_Unknown; @@ -127,6 +128,7 @@ bool enableLinkerRelax() const { return EnableLinkerRelax; } bool enableRVCHintInstrs() const { return EnableRVCHintInstrs; } bool enableSaveRestore() const { return EnableSaveRestore; } + bool disableHardwareIntDiv() const { return DisableHardwareIntDiv; } MVT getXLenVT() const { return XLenVT; } unsigned getXLen() const { return XLen; } RISCVABI::ABI getTargetABI() const { return TargetABI; } Index: llvm/test/CodeGen/RISCV/no-div.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/RISCV/no-div.ll @@ -0,0 +1,45 @@ +; RUN: llc -mtriple=riscv32 -mattr=+m,+no-div -verify-machineinstrs < %s \ +; RUN: | not FileCheck -check-prefix=CHECK-DIV %s +; RUN: llc -mtriple=riscv64 -mattr=+m,+no-div -verify-machineinstrs < %s \ +; RUN: | not FileCheck -check-prefix=CHECK-DIV %s +; RUN: llc -mtriple=riscv32 -mattr=+m,+no-div -verify-machineinstrs < %s \ +; RUN: | not FileCheck -check-prefix=CHECK-REM %s +; RUN: llc -mtriple=riscv64 -mattr=+m,+no-div -verify-machineinstrs < %s \ +; RUN: | not FileCheck -check-prefix=CHECK-REM %s + +; RUN: llc -mtriple=riscv32 -mattr=+m,+no-div -verify-machineinstrs < %s \ +; RUN: | not FileCheck -check-prefix=CHECK-UDIV %s +; RUN: llc -mtriple=riscv64 -mattr=+m,+no-div -verify-machineinstrs < %s \ +; RUN: | not FileCheck -check-prefix=CHECK-UDIV %s +; RUN: llc -mtriple=riscv32 -mattr=+m,+no-div -verify-machineinstrs < %s \ +; RUN: | not FileCheck -check-prefix=CHECK-UREM %s +; RUN: llc -mtriple=riscv64 -mattr=+m,+no-div -verify-machineinstrs < %s \ +; RUN: | not FileCheck -check-prefix=CHECK-UREM %s + +; RUN: llc -mtriple=riscv32 -mattr=+m,+no-div -verify-machineinstrs < %s \ +; RUN: | FileCheck -check-prefix=CHECK-MUL %s +; RUN: llc -mtriple=riscv64 -mattr=+m,+no-div -verify-machineinstrs < %s \ +; RUN: | FileCheck -check-prefix=CHECK-MUL %s + +; RUN: llc -mtriple=riscv32 -mattr=+m -verify-machineinstrs < %s \ +; RUN: | FileCheck -check-prefixes=CHECK-MUL,CHECK-UDIV,CHECK-DIV,CHECK-UREM,CHECK-REM %s +; RUN: llc -mtriple=riscv64 -mattr=+m -verify-machineinstrs < %s \ +; RUN: | FileCheck -check-prefixes=CHECK-MUL,CHECK-UDIV,CHECK-DIV,CHECK-UREM,CHECK-REM %s + +; This test makes sure even when both M extension no-div option enabled, +; the compile would not fail. Instead, it will use a fallback solution like +; calling builtin library functions. + +define i32 @foo(i32 %a, i32 %b) { + ; CHECK-UDIV: divu{{w?}} {{[as]}}{{[0-9]}}, {{[as]}}{{[0-9]}}, {{[as]}}{{[0-9]}} + %1 = udiv i32 %a, %b + ; CHECK-DIV: div{{w?}} {{[as]}}{{[0-9]}}, {{[as]}}{{[0-9]}}, {{[as]}}{{[0-9]}} + %2 = sdiv i32 %a, %1 + ; CHECK-MUL: mul{{w?}} {{[as]}}{{[0-9]}}, {{[as]}}{{[0-9]}}, {{[as]}}{{[0-9]}} + %3 = mul i32 %b, %2 + ; CHECK-UREM: remu{{w?}} {{[as]}}{{[0-9]}}, {{[as]}}{{[0-9]}}, {{[as]}}{{[0-9]}} + %4 = urem i32 %3, %b + ; CHECK-REM: rem {{[as]}}{{[0-9]}}, {{[as]}}{{[0-9]}}, {{[as]}}{{[0-9]}} + %5 = srem i32 %4, %a + ret i32 %5 +} Index: llvm/test/MC/RISCV/rv32m-no-div-div.s =================================================================== --- /dev/null +++ llvm/test/MC/RISCV/rv32m-no-div-div.s @@ -0,0 +1,14 @@ +# RUN: not llvm-mc %s -triple=riscv32 -mattr=+m,+no-div -riscv-no-aliases 2>&1 \ +# RUN: | FileCheck -check-prefixes=CHECK-ERROR %s + +# CHECK-ERROR: 5:1: error: instruction requires the following: Hardware integral division not disabled +div s0, s0, s0 + +# CHECK-ERROR: 8:1: error: instruction requires the following: Hardware integral division not disabled +divu gp, a0, a1 + +# CHECK-ERROR: 11:1: error: instruction requires the following: Hardware integral division not disabled +rem s2, s2, s8 + +# CHECK-ERROR: 14:1: error: instruction requires the following: Hardware integral division not disabled +remu x18, x18, x24 Index: llvm/test/MC/RISCV/rv32m-no-div-mul.s =================================================================== --- /dev/null +++ llvm/test/MC/RISCV/rv32m-no-div-mul.s @@ -0,0 +1,14 @@ +# RUN: llvm-mc %s -triple=riscv32 -mattr=+m,+no-div -riscv-no-aliases 2>&1 \ +# RUN: | FileCheck -check-prefixes=CHECK-INST %s + +# CHECK-INST: mul a4, ra, s0 +mul a4, ra, s0 + +# CHECK-INST: mulh ra, zero, zero +mulh x1, x0, x0 + +# CHECK-INST: mulhsu t0, t2, t1 +mulhsu t0, t2, t1 + +# CHECK-INST: mulhu a5, a4, a3 +mulhu a5, a4, a3 Index: llvm/test/MC/RISCV/rv64m-no-div-div.s =================================================================== --- /dev/null +++ llvm/test/MC/RISCV/rv64m-no-div-div.s @@ -0,0 +1,14 @@ +# RUN: not llvm-mc %s -triple=riscv64 -mattr=+m,+no-div -riscv-no-aliases 2>&1 \ +# RUN: | FileCheck -check-prefixes=CHECK-ERROR %s + +# CHECK-ERROR: 5:1: error: instruction requires the following: Hardware integral division not disabled +divw tp, t0, t1 + +# CHECK-ERROR: 8:1: error: instruction requires the following: Hardware integral division not disabled +divuw t2, s0, s2 + +# CHECK-ERROR: 11:1: error: instruction requires the following: Hardware integral division not disabled +remw a0, a1, a2 + +# CHECK-ERROR: 14:1: error: instruction requires the following: Hardware integral division not disabled +remuw a3, a4, a5 Index: llvm/test/MC/RISCV/rv64m-no-div-mul.s =================================================================== --- /dev/null +++ llvm/test/MC/RISCV/rv64m-no-div-mul.s @@ -0,0 +1,5 @@ +# RUN: llvm-mc %s -triple=riscv64 -mattr=+m,+no-div -riscv-no-aliases 2>&1 \ +# RUN: | FileCheck -check-prefixes=CHECK-INST %s + +# CHECK-INST: mulw ra, sp, gp +mulw ra, sp, gp