diff --git a/llvm/lib/Target/AVR/MCTargetDesc/AVRELFObjectWriter.cpp b/llvm/lib/Target/AVR/MCTargetDesc/AVRELFObjectWriter.cpp --- a/llvm/lib/Target/AVR/MCTargetDesc/AVRELFObjectWriter.cpp +++ b/llvm/lib/Target/AVR/MCTargetDesc/AVRELFObjectWriter.cpp @@ -69,8 +69,13 @@ switch (Modifier) { default: llvm_unreachable("Unsupported Modifier"); - case MCSymbolRefExpr::VK_None: + case MCSymbolRefExpr::VK_None: { + auto &symbol = cast(Target.getSymA()->getSymbol()); + if (symbol.getType() == ELF::STT_NOTYPE) + // Not a global variable, so probably a function pointer. + return ELF::R_AVR_16_PM; return ELF::R_AVR_16; + } case MCSymbolRefExpr::VK_AVR_NONE: return ELF::R_AVR_16_PM; case MCSymbolRefExpr::VK_AVR_DIFF16: diff --git a/llvm/test/CodeGen/AVR/global-relocs.ll b/llvm/test/CodeGen/AVR/global-relocs.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AVR/global-relocs.ll @@ -0,0 +1,42 @@ +; RUN: llc < %s -march=avr -filetype=obj | llvm-objdump -Dr - | FileCheck %s + +; This test checks whether the correct relocation type (R_AVR_16 or +; R_AVR_16_PM) is used for relocations in global variables (such as function +; and global variable pointers). Function pointers need a special relocation +; type so that the linker will use an address in program words (a pointer to +; program space shifted left by one). + +target datalayout = "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8" +target triple = "avr" + +; Some globals to refer to. +@someGlobal = dso_local constant i32 5, align 2 +@someProgmemGlobal = dso_local addrspace(1) constant i16 5, align 2 + +; Reduced testcase from Rust. +@MY_VTABLE = internal constant <{ i8 addrspace(1)* }> <{ i8 addrspace(1)* bitcast (void () addrspace(1)* @wake to i8 addrspace(1)*) }>, align 1 +; CHECK-LABEL: : +; CHECK: R_AVR_16_PM wake + +@ptrToFunction = dso_local constant i8 addrspace(1)* bitcast (void () addrspace(1)* @wake to i8 addrspace(1)*), align 1 +; CHECK-LABEL: : +; CHECK: R_AVR_16_PM wake + +@ptrToGlobal = dso_local constant i32* @someGlobal, align 1 +; CHECK-LABEL: : +; CHECK: R_AVR_16 someGlobal + +@ptrToProgmemGlobal = dso_local constant i16 addrspace(1)* @someProgmemGlobal, align 1 +; CHECK-LABEL: : +; CHECK: R_AVR_16 someProgmemGlobal + +@ptrToProgmemGlobal2 = dso_local constant i8 addrspace(1)* bitcast (i16 addrspace(1)* @someProgmemGlobal to i8 addrspace(1)*), align 1 +; CHECK-LABEL: : +; CHECK: R_AVR_16 someProgmemGlobal + +@ptrToFunction2 = dso_local global void () addrspace(1)* @wake, align 1 +; CHECK-LABEL: : +; CHECK: R_AVR_16_PM wake + +; A function to refer to. +declare dso_local void @wake() addrspace(1) diff --git a/llvm/test/MC/AVR/relocations-abs.s b/llvm/test/MC/AVR/relocations-abs.s --- a/llvm/test/MC/AVR/relocations-abs.s +++ b/llvm/test/MC/AVR/relocations-abs.s @@ -1,8 +1,11 @@ ; RUN: llvm-mc -filetype=obj -triple=avr %s | llvm-objdump -d -r - | FileCheck %s +; WARNING: the behavior below differs from avr-gcc, avr-gcc will use R_AVR_16 +; instead of R_AVR_16_PM. I think this is a bug in avr-gcc. + ; CHECK: : ; CHECK-NEXT: 00 00 nop -; CHECK-NEXT: R_AVR_16 .text+0x2 +; CHECK-NEXT: R_AVR_16_PM .text+0x2 bar: .short 1f 1: