diff --git a/clang/lib/Driver/ToolChains/Arch/M68k.cpp b/clang/lib/Driver/ToolChains/Arch/M68k.cpp index 628c252e8386..349c59546cf2 100644 --- a/clang/lib/Driver/ToolChains/Arch/M68k.cpp +++ b/clang/lib/Driver/ToolChains/Arch/M68k.cpp @@ -104,6 +104,11 @@ void m68k::getM68kTargetFeatures(const Driver &D, const llvm::Triple &Triple, Features.push_back("+reserve-d6"); if (Args.hasArg(options::OPT_ffixed_d7)) Features.push_back("+reserve-d7"); + + // Handle -mpcrel / -mno-pcrel + if (Args.hasFlag(options::OPT_mpcrel, options::OPT_mno_pcrel, + false)) + Features.push_back("+use-pcrel"); } m68k::FloatABI m68k::getM68kFloatABI(const Driver &D, const ArgList &Args) { diff --git a/clang/test/Driver/m68k-features.cpp b/clang/test/Driver/m68k-features.cpp index 0ee9edcfd164..40cc60585ef6 100644 --- a/clang/test/Driver/m68k-features.cpp +++ b/clang/test/Driver/m68k-features.cpp @@ -1,4 +1,11 @@ // REQUIRES: m68k-registered-target + +// ==== PC-Relative Relocation ==== +// RUN: %clang -target m68k -mpcrel -### %s 2> %t +// RUN: FileCheck --check-prefix=CHECK-PCREL < %t %s +// CHECK-PCREL: "-target-feature" "+use-pcrel" + +// ==== Register Reservation ==== // RUN: %clang -target m68k -ffixed-a0 -### %s 2> %t // RUN: FileCheck --check-prefix=CHECK-FIXED-A0 < %t %s // CHECK-FIXED-A0: "-target-feature" "+reserve-a0" diff --git a/llvm/lib/Target/M68k/M68k.td b/llvm/lib/Target/M68k/M68k.td index de7a6c82d110..ae5bc4330a2f 100644 --- a/llvm/lib/Target/M68k/M68k.td +++ b/llvm/lib/Target/M68k/M68k.td @@ -56,6 +56,10 @@ foreach i = {0-7} in SubtargetFeature<"reserve-d"#i, "UserReservedRegister[M68k::D"#i#"]", "true", "Reserve D"#i#" register">; +def FeaturePCRel : + SubtargetFeature<"use-pcrel", "UsePCRelativeRelocation", "true", + "Use pc-relative relocation">; + //===----------------------------------------------------------------------===// // M68k processors supported. //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/M68k/M68kSubtarget.cpp b/llvm/lib/Target/M68k/M68kSubtarget.cpp index e5a4d0d2811b..7b708eec7838 100644 --- a/llvm/lib/Target/M68k/M68kSubtarget.cpp +++ b/llvm/lib/Target/M68k/M68kSubtarget.cpp @@ -148,6 +148,22 @@ unsigned char M68kSubtarget::classifyBlockAddressReference() const { return M68kII::MO_PC_RELATIVE_ADDRESS; } +/// Return relocation kind for potential pc-relative references. +unsigned char M68kSubtarget::usePCRelReference() const { + if (isPCRelative()) + return M68kII::MO_PC_RELATIVE_ADDRESS; + + // BFD LD is unable to resolve pcrel relocations against DSO symbols. + // Thus, we use absolute reloc here just to be safe and (maybe) relax it + // later if the symbol is truely DSO local. + if (TargetTriple.isGNUEnvironment()) { + return isPositionIndependent() ? + M68kII::MO_GOTPCREL : M68kII::MO_ABSOLUTE_ADDRESS; + } else { + return M68kII::MO_PC_RELATIVE_ADDRESS; + } +} + unsigned char M68kSubtarget::classifyLocalReference(const GlobalValue *GV) const { switch (TM.getCodeModel()) { @@ -155,20 +171,20 @@ M68kSubtarget::classifyLocalReference(const GlobalValue *GV) const { llvm_unreachable("Unsupported code model"); case CodeModel::Small: case CodeModel::Kernel: { - return M68kII::MO_PC_RELATIVE_ADDRESS; + return usePCRelReference(); } case CodeModel::Medium: { if (isPositionIndependent()) { // On M68020 and better we can fit big any data offset into dips field. if (atLeastM68020()) { - return M68kII::MO_PC_RELATIVE_ADDRESS; + return usePCRelReference(); } // Otherwise we could check the data size and make sure it will fit into // 16 bit offset. For now we will be conservative and go with @GOTOFF return M68kII::MO_GOTOFF; } else { if (atLeastM68020()) { - return M68kII::MO_PC_RELATIVE_ADDRESS; + return usePCRelReference(); } return M68kII::MO_ABSOLUTE_ADDRESS; } @@ -203,14 +219,14 @@ unsigned char M68kSubtarget::classifyGlobalReference(const GlobalValue *GV, case CodeModel::Kernel: { if (isPositionIndependent()) return M68kII::MO_GOTPCREL; - return M68kII::MO_PC_RELATIVE_ADDRESS; + return usePCRelReference(); } case CodeModel::Medium: { if (isPositionIndependent()) return M68kII::MO_GOTPCREL; if (atLeastM68020()) - return M68kII::MO_PC_RELATIVE_ADDRESS; + return usePCRelReference(); return M68kII::MO_ABSOLUTE_ADDRESS; } diff --git a/llvm/lib/Target/M68k/M68kSubtarget.h b/llvm/lib/Target/M68k/M68kSubtarget.h index c8331d3f091e..795294f0c8e6 100644 --- a/llvm/lib/Target/M68k/M68kSubtarget.h +++ b/llvm/lib/Target/M68k/M68kSubtarget.h @@ -44,6 +44,8 @@ class M68kTargetMachine; class M68kSubtarget : public M68kGenSubtargetInfo { virtual void anchor(); + unsigned char usePCRelReference() const; + protected: // These define which ISA is supported. Since each Motorola M68k ISA is // built on top of the previous one whenever an ISA is selected the previous @@ -55,6 +57,8 @@ class M68kSubtarget : public M68kGenSubtargetInfo { InstrItineraryData InstrItins; + bool UsePCRelativeRelocation = false; + /// Small section is used. bool UseSmallSection = true; @@ -106,6 +110,8 @@ class M68kSubtarget : public M68kGenSubtargetInfo { return UserReservedRegister[R]; } + bool isPCRelative() const { return UsePCRelativeRelocation; } + /// Classify a global variable reference for the current subtarget according /// to how we should reference it in a non-pcrel context. unsigned char classifyLocalReference(const GlobalValue *GV) const; diff --git a/llvm/test/CodeGen/M68k/CodeModel/small-gnu.ll b/llvm/test/CodeGen/M68k/CodeModel/small-gnu.ll new file mode 100644 index 000000000000..618741f62572 --- /dev/null +++ b/llvm/test/CodeGen/M68k/CodeModel/small-gnu.ll @@ -0,0 +1,210 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -O2 -mtriple=m68k-linux-gnu -verify-machineinstrs \ +; RUN: -code-model=small -relocation-model=static \ +; RUN: | FileCheck %s + +; RUN: llc < %s -O2 -mtriple=m68k-linux-gnu -verify-machineinstrs \ +; RUN: -code-model=small -relocation-model=static -mattr="+use-pcrel" \ +; RUN: | FileCheck %s --check-prefix=CHECK-PCREL + +; RUN: llc < %s -O2 -mtriple=m68k-linux-gnu -verify-machineinstrs \ +; RUN: -code-model=small -relocation-model=pic \ +; RUN: | FileCheck %s --check-prefix=CHECK-PIC + +@ptr = external global i32* +@dst = external global i32 +@src = external global i32 + +define void @test0() nounwind { +; CHECK-LABEL: test0: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: move.l #dst, ptr +; CHECK-NEXT: move.l src, dst +; CHECK-NEXT: rts +; +; CHECK-PCREL-LABEL: test0: +; CHECK-PCREL: ; %bb.0: ; %entry +; CHECK-PCREL-NEXT: lea (dst,%pc), %a0 +; CHECK-PCREL-NEXT: move.l %a0, (ptr,%pc) +; CHECK-PCREL-NEXT: move.l (src,%pc), (dst,%pc) +; CHECK-PCREL-NEXT: rts +; +; CHECK-PIC-LABEL: test0: +; CHECK-PIC: ; %bb.0: ; %entry +; CHECK-PIC-NEXT: move.l (dst@GOTPCREL,%pc), %a0 +; CHECK-PIC-NEXT: move.l (ptr@GOTPCREL,%pc), %a1 +; CHECK-PIC-NEXT: move.l %a0, (%a1) +; CHECK-PIC-NEXT: move.l (src@GOTPCREL,%pc), %a1 +; CHECK-PIC-NEXT: move.l (%a1), (%a0) +; CHECK-PIC-NEXT: rts +entry: + store i32* @dst, i32** @ptr + %tmp.s = load i32, i32* @src + store i32 %tmp.s, i32* @dst + ret void +} + +@ptr2 = global i32* null +@dst2 = global i32 0 +@src2 = global i32 0 + +define void @test1() nounwind { +; CHECK-LABEL: test1: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: move.l #dst2, ptr2 +; CHECK-NEXT: move.l src2, dst2 +; CHECK-NEXT: rts +; +; CHECK-PCREL-LABEL: test1: +; CHECK-PCREL: ; %bb.0: ; %entry +; CHECK-PCREL-NEXT: lea (dst2,%pc), %a0 +; CHECK-PCREL-NEXT: move.l %a0, (ptr2,%pc) +; CHECK-PCREL-NEXT: move.l (src2,%pc), (dst2,%pc) +; CHECK-PCREL-NEXT: rts +; +; CHECK-PIC-LABEL: test1: +; CHECK-PIC: ; %bb.0: ; %entry +; CHECK-PIC-NEXT: move.l (dst2@GOTPCREL,%pc), %a0 +; CHECK-PIC-NEXT: move.l (ptr2@GOTPCREL,%pc), %a1 +; CHECK-PIC-NEXT: move.l %a0, (%a1) +; CHECK-PIC-NEXT: move.l (src2@GOTPCREL,%pc), %a1 +; CHECK-PIC-NEXT: move.l (%a1), (%a0) +; CHECK-PIC-NEXT: rts +entry: + store i32* @dst2, i32** @ptr2 + %tmp.s = load i32, i32* @src2 + store i32 %tmp.s, i32* @dst2 + ret void +} + +declare i8* @malloc(i32) + +define void @test2() nounwind { +; CHECK-LABEL: test2: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: suba.l #4, %sp +; CHECK-NEXT: move.l #40, (%sp) +; CHECK-NEXT: jsr malloc@PLT +; CHECK-NEXT: adda.l #4, %sp +; CHECK-NEXT: rts +; +; CHECK-PCREL-LABEL: test2: +; CHECK-PCREL: ; %bb.0: ; %entry +; CHECK-PCREL-NEXT: suba.l #4, %sp +; CHECK-PCREL-NEXT: move.l #40, (%sp) +; CHECK-PCREL-NEXT: jsr malloc@PLT +; CHECK-PCREL-NEXT: adda.l #4, %sp +; CHECK-PCREL-NEXT: rts +; +; CHECK-PIC-LABEL: test2: +; CHECK-PIC: ; %bb.0: ; %entry +; CHECK-PIC-NEXT: suba.l #4, %sp +; CHECK-PIC-NEXT: move.l #40, (%sp) +; CHECK-PIC-NEXT: jsr (malloc@PLT,%pc) +; CHECK-PIC-NEXT: adda.l #4, %sp +; CHECK-PIC-NEXT: rts +entry: + %ptr = call i8* @malloc(i32 40) + ret void +} + +@pfoo = external global void(...)* +declare void(...)* @afoo(...) + +define void @test3() nounwind { +; CHECK-LABEL: test3: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: suba.l #4, %sp +; CHECK-NEXT: jsr afoo@PLT +; CHECK-NEXT: move.l %a0, pfoo +; CHECK-NEXT: jsr (%a0) +; CHECK-NEXT: adda.l #4, %sp +; CHECK-NEXT: rts +; +; CHECK-PCREL-LABEL: test3: +; CHECK-PCREL: ; %bb.0: ; %entry +; CHECK-PCREL-NEXT: suba.l #4, %sp +; CHECK-PCREL-NEXT: jsr afoo@PLT +; CHECK-PCREL-NEXT: move.l %a0, (pfoo,%pc) +; CHECK-PCREL-NEXT: jsr (%a0) +; CHECK-PCREL-NEXT: adda.l #4, %sp +; CHECK-PCREL-NEXT: rts +; +; CHECK-PIC-LABEL: test3: +; CHECK-PIC: ; %bb.0: ; %entry +; CHECK-PIC-NEXT: suba.l #4, %sp +; CHECK-PIC-NEXT: jsr (afoo@PLT,%pc) +; CHECK-PIC-NEXT: move.l (pfoo@GOTPCREL,%pc), %a1 +; CHECK-PIC-NEXT: move.l %a0, (%a1) +; CHECK-PIC-NEXT: jsr (%a0) +; CHECK-PIC-NEXT: adda.l #4, %sp +; CHECK-PIC-NEXT: rts +entry: + %tmp = call void(...)*(...) @afoo() + store void(...)* %tmp, void(...)** @pfoo + %tmp1 = load void(...)*, void(...)** @pfoo + call void(...) %tmp1() + ret void +} + +declare void @foo(...) + +define void @test4() nounwind { +; CHECK-LABEL: test4: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: suba.l #4, %sp +; CHECK-NEXT: jsr foo@PLT +; CHECK-NEXT: adda.l #4, %sp +; CHECK-NEXT: rts +; +; CHECK-PCREL-LABEL: test4: +; CHECK-PCREL: ; %bb.0: ; %entry +; CHECK-PCREL-NEXT: suba.l #4, %sp +; CHECK-PCREL-NEXT: jsr foo@PLT +; CHECK-PCREL-NEXT: adda.l #4, %sp +; CHECK-PCREL-NEXT: rts +; +; CHECK-PIC-LABEL: test4: +; CHECK-PIC: ; %bb.0: ; %entry +; CHECK-PIC-NEXT: suba.l #4, %sp +; CHECK-PIC-NEXT: jsr (foo@PLT,%pc) +; CHECK-PIC-NEXT: adda.l #4, %sp +; CHECK-PIC-NEXT: rts +entry: + call void(...) @foo() + ret void +} + +@ptr6 = internal global i32* null +@dst6 = internal global i32 0 +@src6 = internal global i32 0 + +define void @test5() nounwind { +; CHECK-LABEL: test5: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: move.l #dst6, ptr6 +; CHECK-NEXT: move.l src6, dst6 +; CHECK-NEXT: rts +; +; CHECK-PCREL-LABEL: test5: +; CHECK-PCREL: ; %bb.0: ; %entry +; CHECK-PCREL-NEXT: lea (dst6,%pc), %a0 +; CHECK-PCREL-NEXT: move.l %a0, (ptr6,%pc) +; CHECK-PCREL-NEXT: move.l (src6,%pc), (dst6,%pc) +; CHECK-PCREL-NEXT: rts +; +; CHECK-PIC-LABEL: test5: +; CHECK-PIC: ; %bb.0: ; %entry +; CHECK-PIC-NEXT: move.l (dst6@GOTPCREL,%pc), %a0 +; CHECK-PIC-NEXT: move.l (ptr6@GOTPCREL,%pc), %a1 +; CHECK-PIC-NEXT: move.l %a0, (%a1) +; CHECK-PIC-NEXT: move.l (src6@GOTPCREL,%pc), %a1 +; CHECK-PIC-NEXT: move.l (%a1), (%a0) +; CHECK-PIC-NEXT: rts +entry: + store i32* @dst6, i32** @ptr6 + %tmp.s = load i32, i32* @src6 + store i32 %tmp.s, i32* @dst6 + ret void +} +