anwel created this revision. anwel added reviewers: carwil, amilendra_arm, phosek, michaelplatings, efriedma. anwel added projects: LLVM, clang. Herald added subscribers: llvm-commits, cfe-commits, hiraditya, kristof.beyls.
This patch combines two earlier patches aiming at providing the same support (https://reviews.llvm.org/D56003 for clang, https://reviews.llvm.org/D56005 for LLVM). It enables reservation of allocatable registers via command line options, which in turn allows them to be used as global named register variables. They will then not be used by the register allocator nor spilled to the stack. More information is available in the original RFC: http://lists.llvm.org/pipermail/llvm-dev/2018-December/128706.html Changes from the previous patches include: - adding a constraint to specify -ffixed-rN if rN is used as named register variable. - upgrading the frame-pointer warning to an error and throwing an error in LLVM, as well as clang.* Additionally this patch now only supports r6-r11. r4 and r5 are excluded from this patch as r4 is used as hard-coded scratch register in various parts of the ARM backend. r4 also appears to be used as an input register for a Windows asm routine (__chkstk). Similarly, the ABI of the segmented stack prologue for Android and Linux seems to use r4 and r5 as input registers. A separate patch could follow to add the support for r4 and/or r5, such that the whole range of allocatable registers (r4-r11) is available. As before it should be noted that this also changes the behaviour of the old -ffixed-r9 option. This option will now prevent the register from being spilled to the stack. *This was originally a warning, but we don't seem to have the necessary information to determine frame-pointer usage in the given context. Any insight here would be welcome. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D68862 Files: clang/docs/ClangCommandLineReference.rst clang/include/clang/Basic/DiagnosticDriverKinds.td clang/include/clang/Basic/DiagnosticGroups.td clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Basic/TargetInfo.h clang/include/clang/Driver/Options.td clang/lib/Basic/Targets/ARM.cpp clang/lib/Basic/Targets/ARM.h clang/lib/Driver/ToolChains/Arch/ARM.cpp clang/lib/Sema/SemaDecl.cpp clang/test/Driver/arm-reserved-reg-options.c clang/test/Sema/arm-global-regs.c llvm/lib/Target/ARM/ARM.td llvm/lib/Target/ARM/ARMAsmPrinter.cpp llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp llvm/lib/Target/ARM/ARMFrameLowering.cpp llvm/lib/Target/ARM/ARMISelLowering.cpp llvm/lib/Target/ARM/ARMSubtarget.cpp llvm/lib/Target/ARM/ARMSubtarget.h llvm/lib/Target/ARM/ARMTargetTransformInfo.h llvm/test/CodeGen/ARM/reg-alloc-fixed-r6-vla.ll llvm/test/CodeGen/ARM/reg-alloc-with-fixed-reg-r6-modified.ll llvm/test/CodeGen/ARM/reg-alloc-with-fixed-reg-r6.ll llvm/test/CodeGen/ARM/reg-alloc-wout-fixed-regs.ll llvm/test/CodeGen/Thumb/callee_save_reserved.ll llvm/test/Feature/reserve_global_reg.ll
Index: llvm/test/Feature/reserve_global_reg.ll =================================================================== --- /dev/null +++ llvm/test/Feature/reserve_global_reg.ll @@ -0,0 +1,29 @@ +; RUN: not llc < %s -mtriple=thumbv7-apple-darwin -mattr=+reserve-r7 -o - 2>&1 | FileCheck -check-prefix=CHECK-RESERVE-FP7 %s +; RUN: not llc < %s -mtriple=armv7-windows-msvc -mattr=+reserve-r11 -o - 2>&1 | FileCheck -check-prefix=CHECK-RESERVE-FP11 %s +; RUN: not llc < %s -mtriple=thumbv7-windows -mattr=+reserve-r11 -o - 2>&1 | FileCheck -check-prefix=CHECK-RESERVE-FP11-2 %s + +; int test(int a, int b, int c) { +; return a+b+c; +; } + +; Function Attrs: noinline nounwind optnone +define hidden i32 @_Z4testiii(i32 %a, i32 %b, i32 %c) #0 { +entry: + %a.addr = alloca i32, align 4 + %b.addr = alloca i32, align 4 + %c.addr = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 + store i32 %b, i32* %b.addr, align 4 + store i32 %c, i32* %c.addr, align 4 + %0 = load i32, i32* %a.addr, align 4 + %1 = load i32, i32* %b.addr, align 4 + %add = add nsw i32 %0, %1 + %2 = load i32, i32* %c.addr, align 4 + %add1 = add nsw i32 %add, %2 + ret i32 %add1 +} + +; CHECK-RESERVE-FP7: Register r7 has been specified but is used as a frame pointer on this target. +; CHECK-RESERVE-FP11: Register r11 has been specified but is used as a frame pointer on this target. +; CHECK-RESERVE-FP11-2: Register r11 has been specified but is used as a frame pointer on this target. + Index: llvm/test/CodeGen/Thumb/callee_save_reserved.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/Thumb/callee_save_reserved.ll @@ -0,0 +1,15 @@ +; RUN: llc < %s -mtriple=thumbv6m-none-eabi -verify-machineinstrs -frame-pointer=none -mattr=+reserve-r6,+reserve-r8 \ +; RUN: -asm-verbose=false | FileCheck --check-prefix=CHECK-INVALID %s + +; Reserved low registers should not be used to correct reg deficit. +define <4 x i32> @four_high_four_return_reserved() { +entry: + ; CHECK-INVALID-NOT: r{{6|8}} + tail call void asm sideeffect "", "~{r8},~{r9}"() + %vecinit = insertelement <4 x i32> undef, i32 1, i32 0 + %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1 + %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2 + %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3 + ret <4 x i32> %vecinit13 +} + Index: llvm/test/CodeGen/ARM/reg-alloc-wout-fixed-regs.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/ARM/reg-alloc-wout-fixed-regs.ll @@ -0,0 +1,58 @@ +; RUN: llc < %s -mtriple=arm-linux-gnueabi -O0 -filetype=asm --regalloc=fast 2>&1 | FileCheck %s +; +; Equivalent C source code +; void bar(unsigned int i, +; unsigned int j, +; unsigned int k, +; unsigned int l, +; unsigned int m, +; unsigned int n, +; unsigned int o, +; unsigned int p) +; { +; unsigned int result = i + j + k + l +m + n + o + p; +; } + +define void @bar(i32 %i, i32 %j, i32 %k, i32 %l, i32 %m, i32 %n, i32 %o, i32 %p) nounwind { +entry: +; CHECK: push {{{.*}}r4, r5{{.*}}} + %i.addr = alloca i32, align 4 + %j.addr = alloca i32, align 4 + %k.addr = alloca i32, align 4 + %l.addr = alloca i32, align 4 + %m.addr = alloca i32, align 4 + %n.addr = alloca i32, align 4 + %o.addr = alloca i32, align 4 + %p.addr = alloca i32, align 4 + %result = alloca i32, align 4 + store i32 %i, i32* %i.addr, align 4 + store i32 %j, i32* %j.addr, align 4 + store i32 %k, i32* %k.addr, align 4 + store i32 %l, i32* %l.addr, align 4 + store i32 %m, i32* %m.addr, align 4 + store i32 %n, i32* %n.addr, align 4 + store i32 %o, i32* %o.addr, align 4 + store i32 %p, i32* %p.addr, align 4 + %0 = load i32, i32* %i.addr, align 4 + %1 = load i32, i32* %j.addr, align 4 + %add = add i32 %0, %1 + %2 = load i32, i32* %k.addr, align 4 + %add1 = add i32 %add, %2 + %3 = load i32, i32* %l.addr, align 4 + %add2 = add i32 %add1, %3 + %4 = load i32, i32* %m.addr, align 4 + %add3 = add i32 %add2, %4 + %5 = load i32, i32* %n.addr, align 4 + %add4 = add i32 %add3, %5 + %6 = load i32, i32* %o.addr, align 4 + %add5 = add i32 %add4, %6 + %7 = load i32, i32* %p.addr, align 4 + %add6 = add i32 %add5, %7 + store i32 %add6, i32* %result, align 4 +; CHECK: {{.*}}r4{{.*}} +; CHECK: {{.*}}r5{{.*}} + +; CHECK: pop {{{.*}}r4, r5{{.*}}} + ret void +} + Index: llvm/test/CodeGen/ARM/reg-alloc-with-fixed-reg-r6.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/ARM/reg-alloc-with-fixed-reg-r6.ll @@ -0,0 +1,57 @@ +; RUN: llc < %s -mattr=+reserve-r6 -mtriple=arm-linux-gnueabi -O0 -filetype=asm --regalloc=fast 2>&1 | FileCheck %s +; +; Equivalent C source code +; void bar(unsigned int i, +; unsigned int j, +; unsigned int k, +; unsigned int l, +; unsigned int m, +; unsigned int n, +; unsigned int o, +; unsigned int p) +; { +; unsigned int result = i + j + k + l +m + n + o + p; +; } + +define void @bar(i32 %i, i32 %j, i32 %k, i32 %l, i32 %m, i32 %n, i32 %o, i32 %p) nounwind { +entry: +; CHECK-NOT: push {{{.*}}r6,{{.*}}} + %i.addr = alloca i32, align 4 + %j.addr = alloca i32, align 4 + %k.addr = alloca i32, align 4 + %l.addr = alloca i32, align 4 + %m.addr = alloca i32, align 4 + %n.addr = alloca i32, align 4 + %o.addr = alloca i32, align 4 + %p.addr = alloca i32, align 4 + %result = alloca i32, align 4 + store i32 %i, i32* %i.addr, align 4 + store i32 %j, i32* %j.addr, align 4 + store i32 %k, i32* %k.addr, align 4 + store i32 %l, i32* %l.addr, align 4 + store i32 %m, i32* %m.addr, align 4 + store i32 %n, i32* %n.addr, align 4 + store i32 %o, i32* %o.addr, align 4 + store i32 %p, i32* %p.addr, align 4 + %0 = load i32, i32* %i.addr, align 4 + %1 = load i32, i32* %j.addr, align 4 + %add = add i32 %0, %1 + %2 = load i32, i32* %k.addr, align 4 + %add1 = add i32 %add, %2 + %3 = load i32, i32* %l.addr, align 4 + %add2 = add i32 %add1, %3 + %4 = load i32, i32* %m.addr, align 4 + %add3 = add i32 %add2, %4 + %5 = load i32, i32* %n.addr, align 4 + %add4 = add i32 %add3, %5 + %6 = load i32, i32* %o.addr, align 4 + %add5 = add i32 %add4, %6 + %7 = load i32, i32* %p.addr, align 4 + %add6 = add i32 %add5, %7 + store i32 %add6, i32* %result, align 4 +; CHECK: {{.*}}r5{{.*}} +; CHECK-NOT: {{.*}}r6{{.*}} + ret void +; CHECK-NOT: pop {{{.*}}r6,{{.*}}} +} + Index: llvm/test/CodeGen/ARM/reg-alloc-with-fixed-reg-r6-modified.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/ARM/reg-alloc-with-fixed-reg-r6-modified.ll @@ -0,0 +1,63 @@ +; RUN: llc < %s -mattr=+reserve-r6 -mtriple=arm-linux-gnueabi -O0 -filetype=asm --regalloc=fast 2>&1 | FileCheck %s +; +; Equivalent C source code +; register unsigned r6 asm("r6"); +; void bar(unsigned int i, +; unsigned int j, +; unsigned int k, +; unsigned int l, +; unsigned int m, +; unsigned int n, +; unsigned int o, +; unsigned int p) +; { +; r6 = 10; +; unsigned int result = i + j + k + l +m + n + o + p; +; } +declare void @llvm.write_register.i32(metadata, i32) nounwind + +define void @bar(i32 %i, i32 %j, i32 %k, i32 %l, i32 %m, i32 %n, i32 %o, i32 %p) nounwind { +entry: +; CHECK-NOT: push {{{.*}}r6,{{.*}}} +; CHECK: {{.*}}mov{{.*}}r6,{{.*}} +; CHECK-NOT: {{.*}}r6{{.*}} + %i.addr = alloca i32, align 4 + %j.addr = alloca i32, align 4 + %k.addr = alloca i32, align 4 + %l.addr = alloca i32, align 4 + %m.addr = alloca i32, align 4 + %n.addr = alloca i32, align 4 + %o.addr = alloca i32, align 4 + %p.addr = alloca i32, align 4 + %result = alloca i32, align 4 + store i32 %i, i32* %i.addr, align 4 + store i32 %j, i32* %j.addr, align 4 + store i32 %k, i32* %k.addr, align 4 + store i32 %l, i32* %l.addr, align 4 + store i32 %m, i32* %m.addr, align 4 + store i32 %n, i32* %n.addr, align 4 + store i32 %o, i32* %o.addr, align 4 + store i32 %p, i32* %p.addr, align 4 + call void @llvm.write_register.i32(metadata !0, i32 10) + %0 = load i32, i32* %i.addr, align 4 + %1 = load i32, i32* %j.addr, align 4 + %add = add i32 %0, %1 + %2 = load i32, i32* %k.addr, align 4 + %add1 = add i32 %add, %2 + %3 = load i32, i32* %l.addr, align 4 + %add2 = add i32 %add1, %3 + %4 = load i32, i32* %m.addr, align 4 + %add3 = add i32 %add2, %4 + %5 = load i32, i32* %n.addr, align 4 + %add4 = add i32 %add3, %5 + %6 = load i32, i32* %o.addr, align 4 + %add5 = add i32 %add4, %6 + %7 = load i32, i32* %p.addr, align 4 + %add6 = add i32 %add5, %7 + store i32 %add6, i32* %result, align 4 + ret void +} + +!llvm.named.register.r6 = !{!0} +!0 = !{!"r6"} + Index: llvm/test/CodeGen/ARM/reg-alloc-fixed-r6-vla.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/ARM/reg-alloc-fixed-r6-vla.ll @@ -0,0 +1,44 @@ +; Using VLAs(Variable Length Arrays) in a function will use R6 to keep track +; of the stack frame, and also spill/restore R6 to the stack. +; This tests that using -ffixed-r6 (-mattr=+reserve-r6) will stop R6 +; being used and also stop it being spilled/restored to the stack. +; RUN: llc < %s -mcpu=cortex-m0 -mtriple=thumbv7-arm-none-eabi | FileCheck %s --check-prefix=CHECK-STATIC --check-prefix=CHECK-R6 +; RUN: llc < %s -mcpu=cortex-m0 -mtriple=thumbv7-arm-none-eabi -mattr=+reserve-r6 | FileCheck %s --check-prefix=CHECK-STATIC --check-prefix=CHECK-NO-R6 + +define void @f() #0 { +entry: + %i = alloca i32, align 4 + store i32 0, i32* %i, align 4 + + %saved_stack = alloca i8*, align 4 + %0 = call i8* @llvm.stacksave() + store i8* %0, i8** %saved_stack, align 4 + + %__vla_expr0 = alloca i32, align 4 + %1 = load i32, i32* %i, align 4 + %vla = alloca double, i32 %1, align 8 + store i32 %1, i32* %__vla_expr0, align 4 + + %2 = load i8*, i8** %saved_stack, align 4 + call void @llvm.stackrestore(i8* %2) + + ret void +} + +declare i8* @llvm.stacksave() #1 +declare void @llvm.stackrestore(i8* %ptr) #1 + +attributes #0 = { noinline nounwind "stackrealign" } +attributes #1 = { nounwind } + +; CHECK-STATIC: push {r4, +; CHECK-R6: r6 +; CHECK-NO-R6-NOT: r6 +; CHECK-STATIC: lr} +; CHECK-R6: r6 +; CHECK-NO-R6-NOT: r6 +; CHECK-STATIC: pop {r4, +; CHECK-R6: r6 +; CHECK-NO-R6-NOT: r6 +; CHECK-STATIC: pc} + Index: llvm/lib/Target/ARM/ARMTargetTransformInfo.h =================================================================== --- llvm/lib/Target/ARM/ARMTargetTransformInfo.h +++ llvm/lib/Target/ARM/ARMTargetTransformInfo.h @@ -76,7 +76,9 @@ ARM::FeatureDSP, ARM::FeatureMP, ARM::FeatureVirtualization, ARM::FeatureMClass, ARM::FeatureRClass, ARM::FeatureAClass, ARM::FeatureNaClTrap, ARM::FeatureStrictAlign, ARM::FeatureLongCalls, - ARM::FeatureExecuteOnly, ARM::FeatureReserveR9, ARM::FeatureNoMovt, + ARM::FeatureExecuteOnly, ARM::FeatureReserveR6, ARM::FeatureReserveR7, + ARM::FeatureReserveR8, ARM::FeatureReserveR9, ARM::FeatureReserveR10, + ARM::FeatureReserveR11, ARM::FeatureNoMovt, ARM::FeatureNoNegativeImmediates }; Index: llvm/lib/Target/ARM/ARMSubtarget.h =================================================================== --- llvm/lib/Target/ARM/ARMSubtarget.h +++ llvm/lib/Target/ARM/ARMSubtarget.h @@ -232,8 +232,8 @@ /// NoARM - True if subtarget does not support ARM mode execution. bool NoARM = false; - /// ReserveR9 - True if R9 is not available as a general purpose register. - bool ReserveR9 = false; + // ReservedRRegisters[i] - R#i is not available as a general purpose register. + BitVector ReservedRRegisters; /// NoMovt - True if MOVT / MOVW pairs are not used for materialization of /// 32-bit imms (including global addresses). @@ -760,8 +760,9 @@ bool isAClass() const { return ARMProcClass == AClass; } bool isReadTPHard() const { return ReadTPHard; } - bool isR9Reserved() const { - return isTargetMachO() ? (ReserveR9 || !HasV6Ops) : ReserveR9; + bool isRRegisterReserved(size_t i) const { return ReservedRRegisters[i]; } + unsigned getNumRRegisterReserved() const { + return ReservedRRegisters.count(); } bool useR7AsFramePointer() const { Index: llvm/lib/Target/ARM/ARMSubtarget.cpp =================================================================== --- llvm/lib/Target/ARM/ARMSubtarget.cpp +++ llvm/lib/Target/ARM/ARMSubtarget.cpp @@ -95,8 +95,9 @@ const ARMBaseTargetMachine &TM, bool IsLittle, bool MinSize) : ARMGenSubtargetInfo(TT, CPU, FS), UseMulOps(UseFusedMulOps), - CPUString(CPU), OptMinSize(MinSize), IsLittle(IsLittle), - TargetTriple(TT), Options(TM.Options), TM(TM), + ReservedRRegisters(ARM::GPRRegClass.getNumRegs()), CPUString(CPU), + OptMinSize(MinSize), IsLittle(IsLittle), TargetTriple(TT), + Options(TM.Options), TM(TM), FrameLowering(initializeFrameLowering(CPU, FS)), // At this point initializeSubtargetDependencies has been called so // we can query directly. @@ -250,8 +251,18 @@ (Options.UnsafeFPMath || isTargetDarwin())) UseNEONForSinglePrecisionFP = true; - if (isRWPI()) - ReserveR9 = true; + if (isRWPI() || (isTargetMachO() && !HasV6Ops)) + ReservedRRegisters.set(9); + + // Throw an error when trying to reserve a target's FP register. It may + // be used by the compiler even when frame pointer elimination is enabled. + // FIXME: Throw this error if -frame-pointer=none is not set; otherwise + // only emit a warning. + const int restFP = (useR7AsFramePointer()) ? 7 : 11; + if (isRRegisterReserved(restFP)) + report_fatal_error( + "Register r" + std::to_string(restFP) + + " has been specified but is used as a frame pointer on this target."); // If MVEVectorCostFactor is still 0 (has not been set to anything else), default it to 2 if (MVEVectorCostFactor == 0) Index: llvm/lib/Target/ARM/ARMISelLowering.cpp =================================================================== --- llvm/lib/Target/ARM/ARMISelLowering.cpp +++ llvm/lib/Target/ARM/ARMISelLowering.cpp @@ -5474,9 +5474,15 @@ Register ARMTargetLowering::getRegisterByName(const char* RegName, EVT VT, const MachineFunction &MF) const { Register Reg = StringSwitch<unsigned>(RegName) - .Case("sp", ARM::SP) - .Default(0); - if (Reg) + .Case("r6", ARM::R6) + .Case("r7", ARM::R7) + .Case("r8", ARM::R8) + .Case("r9", ARM::R9) + .Case("r10", ARM::R10) + .Case("r11", ARM::R11) + .Case("sp", ARM::SP) + .Default(ARM::NoRegister); + if (Reg != ARM::NoRegister) return Reg; report_fatal_error(Twine("Invalid register name \"" + StringRef(RegName) + "\".")); Index: llvm/lib/Target/ARM/ARMFrameLowering.cpp =================================================================== --- llvm/lib/Target/ARM/ARMFrameLowering.cpp +++ llvm/lib/Target/ARM/ARMFrameLowering.cpp @@ -1704,6 +1704,19 @@ const MCPhysReg *CSRegs = RegInfo->getCalleeSavedRegs(&MF); for (unsigned i = 0; CSRegs[i]; ++i) { unsigned Reg = CSRegs[i]; + if (STI.isRWPI() && Reg == ARM::R9) { + // Paranoid check for use of R9 with RWPI. Clobbering R9 with -frwpi will + // emit warnings about undefined behaviour but maybe theres's a valid use + // case so on that basis allow it to be pushed/popped in the + // prologue/epilogue. + } else if (Reg > ARM::R0 && ARM::GPRRegClass.contains(Reg) && + STI.isRRegisterReserved(Reg - ARM::R0)) { + LLVM_DEBUG(dbgs() << printReg(Reg, TRI) << " has been reserved and" + << " should not be allocatable" + << " or spillable.\n"); + SavedRegs.reset(Reg); + continue; + } bool Spilled = false; if (SavedRegs.test(Reg)) { Spilled = true; @@ -1948,7 +1961,7 @@ LLVM_DEBUG(dbgs() << printReg(Reg, TRI) << " is saved low register, RegDeficit = " << RegDeficit << "\n"); - } else { + } else if (!STI.isRRegisterReserved(Reg - ARM::R0)) { AvailableRegs.push_back(Reg); LLVM_DEBUG( dbgs() @@ -1963,7 +1976,7 @@ --RegDeficit; LLVM_DEBUG(dbgs() << "%r7 is saved low register, RegDeficit = " << RegDeficit << "\n"); - } else { + } else if (!STI.isRRegisterReserved(7)) { AvailableRegs.push_back(ARM::R7); LLVM_DEBUG( dbgs() Index: llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp =================================================================== --- llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp +++ llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp @@ -195,9 +195,11 @@ markSuperRegs(Reserved, getFramePointerReg(STI)); if (hasBasePointer(MF)) markSuperRegs(Reserved, BasePtr); - // Some targets reserve R9. - if (STI.isR9Reserved()) - markSuperRegs(Reserved, ARM::R9); + for (size_t R = 0; R < ARM::GPRRegClass.getNumRegs(); ++R) { + if (STI.isRRegisterReserved(R)) { + markSuperRegs(Reserved, ARM::R0 + R); + } + } // Reserve D16-D31 if the subtarget doesn't support them. if (!STI.hasD32()) { static_assert(ARM::D31 == ARM::D16 + 15, "Register list not consecutive!"); @@ -277,7 +279,7 @@ case ARM::GPRRegClassID: { bool HasFP = MF.getFrameInfo().isMaxCallFrameSizeComputed() ? TFI->hasFP(MF) : true; - return 10 - HasFP - (STI.isR9Reserved() ? 1 : 0); + return 10 - HasFP - STI.getNumRRegisterReserved(); } case ARM::SPRRegClassID: // Currently not used as 'rep' register class. case ARM::DPRRegClassID: @@ -377,6 +379,11 @@ const MachineFrameInfo &MFI = MF.getFrameInfo(); const ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>(); const ARMFrameLowering *TFI = getFrameLowering(MF); + const ARMSubtarget &STI = MF.getSubtarget<ARMSubtarget>(); + + // Disable base pointer R6 if -ffixed-r6 is used. + if (STI.isRRegisterReserved(BasePtr - ARM::R0)) + return false; // If we have stack realignment and VLAs, we have no pointer to use to // access the stack. If we have stack realignment, and a large call frame, @@ -413,6 +420,7 @@ bool ARMBaseRegisterInfo::canRealignStack(const MachineFunction &MF) const { const MachineRegisterInfo *MRI = &MF.getRegInfo(); const ARMFrameLowering *TFI = getFrameLowering(MF); + const ARMSubtarget &STI = MF.getSubtarget<ARMSubtarget>(); // We can't realign the stack if: // 1. Dynamic stack realignment is explicitly disabled, // 2. There are VLAs in the function and the base pointer is disabled. @@ -422,6 +430,9 @@ // register allocation with frame pointer elimination, it is too late now. if (!MRI->canReserveReg(getFramePointerReg(MF.getSubtarget<ARMSubtarget>()))) return false; + // Disable base pointer R6 if -ffixed-r6 is used. + if (STI.isRRegisterReserved(BasePtr - ARM::R0)) + return false; // We may also need a base pointer if there are dynamic allocas or stack // pointer adjustments around calls. if (TFI->hasReservedCallFrame(MF)) Index: llvm/lib/Target/ARM/ARMAsmPrinter.cpp =================================================================== --- llvm/lib/Target/ARM/ARMAsmPrinter.cpp +++ llvm/lib/Target/ARM/ARMAsmPrinter.cpp @@ -752,7 +752,7 @@ if (STI.isRWPI()) ATS.emitAttribute(ARMBuildAttrs::ABI_PCS_R9_use, ARMBuildAttrs::R9IsSB); - else if (STI.isR9Reserved()) + else if (STI.isRRegisterReserved(9)) ATS.emitAttribute(ARMBuildAttrs::ABI_PCS_R9_use, ARMBuildAttrs::R9Reserved); else Index: llvm/lib/Target/ARM/ARM.td =================================================================== --- llvm/lib/Target/ARM/ARM.td +++ llvm/lib/Target/ARM/ARM.td @@ -391,9 +391,11 @@ "Enable the generation of " "execute only code.">; -def FeatureReserveR9 : SubtargetFeature<"reserve-r9", "ReserveR9", "true", - "Reserve R9, making it unavailable" - " as GPR">; +foreach i = {6-11} in + def FeatureReserveR#i : SubtargetFeature<"reserve-r"#i, + "ReservedRRegisters["#i#"]", "true", + "Reserve R"#i#", making it " + "unavailable as a GPR">; def FeatureNoMovt : SubtargetFeature<"no-movt", "NoMovt", "true", "Don't use movt/movw pairs for " Index: clang/test/Sema/arm-global-regs.c =================================================================== --- /dev/null +++ clang/test/Sema/arm-global-regs.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -ffreestanding -fsyntax-only -target-feature +reserve-r9 -verify -triple arm-arm-none-eabi %s + +// Check a small subset of valid and invalid global register variable declarations. +// Also check that for global register variables without -ffixed-reg options it throws an error. + +register unsigned arm_r3 __asm("r3"); //expected-error {{register 'r3' unsuitable for global register variables on this target}} + +register unsigned arm_r12 __asm("r12"); //expected-error {{register 'r12' unsuitable for global register variables on this target}} + +register unsigned arm_r5 __asm("r5"); //expected-error {{register 'r5' unsuitable for global register variables on this target}} + +register unsigned arm_r9 __asm("r9"); + +register unsigned arm_r6 __asm("r6"); //expected-error {{-ffixed-r6 is required for global named register variable declaration}} + +register unsigned arm_r7 __asm("r7"); //expected-error {{-ffixed-r7 is required for global named register variable declaration}} + +register unsigned *parm_r7 __asm("r7"); //expected-error {{-ffixed-r7 is required for global named register variable declaration}} + +register unsigned arm_sp __asm("sp"); Index: clang/test/Driver/arm-reserved-reg-options.c =================================================================== --- /dev/null +++ clang/test/Driver/arm-reserved-reg-options.c @@ -0,0 +1,35 @@ +// ## FP ARM + Thumb +// RUN: %clang -target arm-arm-none-eabi -### -ffixed-r11 -c %s 2>&1 | FileCheck -check-prefix=CHECK-ERROR-R11 %s +// RUN: %clang -target arm-arm-none-eabi -### -ffixed-r7 -c %s 2>&1 | FileCheck -check-prefix=CHECK-NO-ERROR %s + +// RUN: %clang -target arm-arm-none-eabi -### -ffixed-r7 -mthumb -c %s 2>&1 | FileCheck -check-prefix=CHECK-ERROR-R7 %s +// RUN: %clang -target arm-arm-none-eabi -### -ffixed-r11 -mthumb -c %s 2>&1 | FileCheck -check-prefix=CHECK-NO-ERROR %s + +// RUN: %clang -target thumbv6m-none-eabi -### -ffixed-r7 -c %s 2>&1 | FileCheck -check-prefix=CHECK-ERROR-R7 %s +// RUN: %clang -target thumbv6m-none-eabi -### -ffixed-r11 -c %s 2>&1 | FileCheck -check-prefix=CHECK-NO-ERROR %s + +// ## FP Darwin (R7) +// RUN: %clang -target armv6-apple-darwin9 -### -ffixed-r7 -c %s 2>&1 | FileCheck -check-prefix=CHECK-ERROR-R7 %s +// RUN: %clang -target armv6-apple-darwin9 -### -ffixed-r11 -c %s 2>&1 | FileCheck -check-prefix=CHECK-NO-ERROR %s + +// RUN: %clang -target armv6-apple-ios3 -### -ffixed-r7 -c %s 2>&1 | FileCheck -check-prefix=CHECK-ERROR-R7 %s +// RUN: %clang -target armv6-apple-ios3 -### -ffixed-r11 -c %s 2>&1 | FileCheck -check-prefix=CHECK-NO-ERROR %s + +// RUN: %clang -target armv7s-apple-darwin10 -### -ffixed-r7 -c %s 2>&1 | FileCheck -check-prefix=CHECK-ERROR-R7 %s +// RUN: %clang -target armv7s-apple-darwin10 -### -ffixed-r11 -c %s 2>&1 | FileCheck -check-prefix=CHECK-NO-ERROR %s + +// ## FP Windows (R11) +// RUN: %clang -target armv7-windows -### -ffixed-r11 -c %s 2>&1 | FileCheck -check-prefix=CHECK-ERROR-R11 %s +// RUN: %clang -target armv7-windows -### -ffixed-r7 -c %s 2>&1 | FileCheck -check-prefix=CHECK-NO-ERROR %s + +// ## FRWPI (R9) +// RUN: %clang -target arm-arm-none-eabi -### -frwpi -ffixed-r9 -c %s 2>&1 | FileCheck -check-prefix=CHECK-RESERVED-FRWPI-CONFLICT %s +// RUN: %clang -target arm-arm-none-eabi -### -ffixed-r9 -c %s 2>&1 | FileCheck -check-prefix=CHECK-RESERVED-FRWPI-VALID %s +// RUN: %clang -target arm-arm-none-eabi -### -frwpi -c %s 2>&1 | FileCheck -check-prefix=CHECK-RESERVED-FRWPI-VALID %s + +// CHECK-ERROR-R11: error: '-ffixed-r11' has been specified but 'r11' may still be used as a frame pointer +// CHECK-ERROR-R7: error: '-ffixed-r7' has been specified but 'r7' may still be used as a frame pointer +// CHECK-NO-ERROR-NOT: may still be used as a frame pointer + +// CHECK-RESERVED-FRWPI-CONFLICT: option '-ffixed-r9' cannot be specified with '-frwpi' +// CHECK-RESERVED-FRWPI-VALID-NOT: option '-ffixed-r9' cannot be specified with '-frwpi' Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -6979,6 +6979,8 @@ Diag(E->getExprLoc(), diag::err_asm_invalid_global_var_reg) << Label; else if (HasSizeMismatch) Diag(E->getExprLoc(), diag::err_asm_register_size_mismatch) << Label; + else if (!TI.isRegisterReservedGlobally(Label)) + Diag(E->getExprLoc(), diag::err_asm_missing_fixed_reg_opt) << Label; } if (!R->isIntegralType(Context) && !R->isPointerType()) { Index: clang/lib/Driver/ToolChains/Arch/ARM.cpp =================================================================== --- clang/lib/Driver/ToolChains/Arch/ARM.cpp +++ clang/lib/Driver/ToolChains/Arch/ARM.cpp @@ -581,11 +581,39 @@ Features.push_back("+strict-align"); } - // llvm does not support reserving registers in general. There is support - // for reserving r9 on ARM though (defined as a platform-specific register - // in ARM EABI). - if (Args.hasArg(options::OPT_ffixed_r9)) - Features.push_back("+reserve-r9"); + // Do not allow r9 reservation with -frwpi. + if (Args.hasArg(options::OPT_ffixed_r9) && Args.hasArg(options::OPT_frwpi)) { + Arg *A = Args.getLastArg(options::OPT_ffixed_r9); + Arg *B = Args.getLastArg(options::OPT_frwpi); + D.Diag(diag::err_opt_not_valid_with_opt) + << A->getAsString(Args) << B->getAsString(Args); + } + + // The compiler can still use a FP in certain circumstances, + // even when frame pointer elimination is enabled. Thus we should + // not allow to reserve a target's FP register. + const llvm::opt::OptSpecifier RestrictFPOpt = + (Triple.isOSDarwin() || (!Triple.isOSWindows() && Triple.isThumb())) + ? options::OPT_ffixed_r7 + : options::OPT_ffixed_r11; + if (Args.hasArg(RestrictFPOpt)) { + const std::string OptStr = + Args.getLastArg(RestrictFPOpt)->getAsString(Args); + const unsigned int SubStrIndex = strlen("ffixed-r"); + D.Diag(diag::err_reserved_frame_pointer) + << OptStr << OptStr.substr(SubStrIndex); + } + +// Reservation of general purpose registers. +#define HANDLE_FFIXED_R(n) \ + if (Args.hasArg(options::OPT_ffixed_r##n)) \ + Features.push_back("+reserve-r" #n) + HANDLE_FFIXED_R(6); + HANDLE_FFIXED_R(7); + HANDLE_FFIXED_R(8); + HANDLE_FFIXED_R(9); + HANDLE_FFIXED_R(10); + HANDLE_FFIXED_R(11); // The kext linker doesn't know how to deal with movw/movt. if (KernelOrKext || Args.hasArg(options::OPT_mno_movt)) Index: clang/lib/Basic/Targets/ARM.h =================================================================== --- clang/lib/Basic/Targets/ARM.h +++ clang/lib/Basic/Targets/ARM.h @@ -161,6 +161,9 @@ ArrayRef<const char *> getGCCRegNames() const override; ArrayRef<TargetInfo::GCCRegAlias> getGCCRegAliases() const override; + bool validateGlobalRegisterVariable(StringRef RegName, unsigned RegSize, + bool &HasSizeMismatch) const override; + bool isRegisterReservedGlobally(StringRef RegName) const override; bool validateAsmConstraint(const char *&Name, TargetInfo::ConstraintInfo &Info) const override; std::string convertConstraint(const char *&Constraint) const override; Index: clang/lib/Basic/Targets/ARM.cpp =================================================================== --- clang/lib/Basic/Targets/ARM.cpp +++ clang/lib/Basic/Targets/ARM.cpp @@ -879,6 +879,34 @@ return llvm::makeArrayRef(GCCRegAliases); } +bool ARMTargetInfo::validateGlobalRegisterVariable( + StringRef RegName, unsigned RegSize, bool &HasSizeMismatch) const { + if (RegName.equals("r6") || RegName.equals("r7") || RegName.equals("r8") || + RegName.equals("r9") || RegName.equals("r10") || RegName.equals("r11") || + RegName.equals("sp")) { + HasSizeMismatch = false; + return true; + } + return false; +} + +bool ARMTargetInfo::isRegisterReservedGlobally(StringRef RegName) const { + // The "sp" register does not have a -ffixed-sp option, + // so enable it unconditionally. + if (RegName.equals("sp")) + return true; + + // enable rN (N:6-11) registers only if the corresponding + // +reserve-rN feature is found + std::vector<std::string> &Features = getTargetOpts().Features; + std::string SearchFeature = "+reserve-" + RegName.str(); + for (std::string &Feature : Features) { + if (Feature.compare(SearchFeature) == 0) + return true; + } + return false; +} + bool ARMTargetInfo::validateAsmConstraint( const char *&Name, TargetInfo::ConstraintInfo &Info) const { switch (*Name) { Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -2205,8 +2205,9 @@ def mno_restrict_it: Flag<["-"], "mno-restrict-it">, Group<m_arm_Features_Group>, HelpText<"Allow generation of deprecated IT blocks for ARMv8. It is off by default for ARMv8 Thumb mode">; def marm : Flag<["-"], "marm">, Alias<mno_thumb>; -def ffixed_r9 : Flag<["-"], "ffixed-r9">, Group<m_arm_Features_Group>, - HelpText<"Reserve the r9 register (ARM only)">; +foreach i = {6-11} in + def ffixed_r#i : Flag<["-"], "ffixed-r"#i>, Group<m_arm_Features_Group>, + HelpText<"Reserve the r"#i#" register (ARM only)">; def mno_movt : Flag<["-"], "mno-movt">, Group<m_arm_Features_Group>, HelpText<"Disallow use of movt/movw pairs (ARM only)">; def mcrc : Flag<["-"], "mcrc">, Group<m_Group>, Index: clang/include/clang/Basic/TargetInfo.h =================================================================== --- clang/include/clang/Basic/TargetInfo.h +++ clang/include/clang/Basic/TargetInfo.h @@ -937,6 +937,14 @@ return true; } + /// Check if the register is reserved globally + /// + /// This function returns true if the register passed in RegName is reserved + /// using the corresponding -ffixed-RegName option. + virtual bool isRegisterReservedGlobally(StringRef RegName) const { + return true; + } + // validateOutputConstraint, validateInputConstraint - Checks that // a constraint is valid and provides information about it. // FIXME: These should return a real error instead of just true/false. Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7677,6 +7677,8 @@ def err_asm_unknown_register_name : Error<"unknown register name '%0' in asm">; def err_asm_invalid_global_var_reg : Error<"register '%0' unsuitable for " "global register variables on this target">; + def err_asm_missing_fixed_reg_opt : Error<"-ffixed-%0 is required for " + "global named register variable declaration">; def err_asm_register_size_mismatch : Error<"size of register '%0' does not " "match variable size">; def err_asm_bad_register_type : Error<"bad type for named register variable">; Index: clang/include/clang/Basic/DiagnosticGroups.td =================================================================== --- clang/include/clang/Basic/DiagnosticGroups.td +++ clang/include/clang/Basic/DiagnosticGroups.td @@ -1105,3 +1105,6 @@ def CTADMaybeUnsupported : DiagGroup<"ctad-maybe-unsupported">; def FortifySource : DiagGroup<"fortify-source">; + +// Register reservation. +def FixedRegs : DiagGroup<"fixed-registers">; Index: clang/include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticDriverKinds.td +++ clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -462,6 +462,10 @@ "specify a MSP430 device, or -mhwmult to set hardware multiply type " "explicitly.">, InGroup<InvalidCommandLineArgument>; +// Frame pointer reservation. +def err_reserved_frame_pointer : Error< + "'%0' has been specified but '%1' may still be used as a frame pointer">; + def warn_drv_libstdcxx_not_found : Warning< "include path for libstdc++ headers not found; pass '-stdlib=libc++' on the " "command line to use the libc++ standard library instead">, Index: clang/docs/ClangCommandLineReference.rst =================================================================== --- clang/docs/ClangCommandLineReference.rst +++ clang/docs/ClangCommandLineReference.rst @@ -2426,10 +2426,31 @@ ARM --- + +.. option:: -ffixed-r6 + +Reserve the r6 register (ARM only) + +.. option:: -ffixed-r7 + +Reserve the r7 register (ARM only) + +.. option:: -ffixed-r8 + +Reserve the r8 register (ARM only) + .. option:: -ffixed-r9 Reserve the r9 register (ARM only) +.. option:: -ffixed-r10 + +Reserve the r10 register (ARM only) + +.. option:: -ffixed-r11 + +Reserve the r11 register (ARM only) + .. option:: -mexecute-only, -mno-execute-only, -mpure-code Disallow generation of data access to code sections (ARM only)
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits