diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp @@ -11,10 +11,12 @@ //===----------------------------------------------------------------------===// #include "LoongArchAsmBackend.h" +#include "LoongArchFixupKinds.h" #include "llvm/MC/MCAsmLayout.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCValue.h" #include "llvm/Support/Endian.h" #include "llvm/Support/EndianStream.h" @@ -75,6 +77,52 @@ return Infos[Kind - FirstTargetFixupKind]; } +static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, + MCContext &Ctx) { + switch (Fixup.getTargetKind()) { + default: + llvm_unreachable("Unknown fixup kind"); + case FK_Data_1: + case FK_Data_2: + case FK_Data_4: + case FK_Data_8: + return Value; + case LoongArch::fixup_loongarch_b16: { + if (!isInt<18>(Value)) + Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); + if (Value % 4) + Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned"); + return (Value >> 2) & 0xffff; + } + case LoongArch::fixup_loongarch_b21: { + if (!isInt<23>(Value)) + Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); + if (Value % 4) + Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned"); + return ((Value & 0x3fffc) << 8) | ((Value >> 18) & 0x1f); + } + case LoongArch::fixup_loongarch_b26: { + if (!isInt<28>(Value)) + Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); + if (Value % 4) + Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned"); + return ((Value & 0x3fffc) << 8) | ((Value >> 18) & 0x3ff); + } + case LoongArch::fixup_loongarch_abs_hi20: + case LoongArch::fixup_loongarch_tls_le_hi20: + return (Value >> 12) & 0xfffff; + case LoongArch::fixup_loongarch_abs_lo12: + case LoongArch::fixup_loongarch_tls_le_lo12: + return Value & 0xfff; + case LoongArch::fixup_loongarch_abs64_lo20: + case LoongArch::fixup_loongarch_tls_le64_lo20: + return (Value >> 32) & 0xfffff; + case LoongArch::fixup_loongarch_abs64_hi12: + case LoongArch::fixup_loongarch_tls_le64_hi12: + return (Value >> 52) & 0xfff; + } +} + void LoongArchAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, @@ -88,7 +136,10 @@ if (Kind >= FirstLiteralRelocationKind) return; MCFixupKindInfo Info = getFixupKindInfo(Kind); - // TODO: Apply any target-specific value adjustments. + MCContext &Ctx = Asm.getContext(); + + // Apply any target-specific value adjustments. + Value = adjustFixupValue(Fixup, Value, Ctx); // Shift the value into position. Value <<= Info.TargetOffset; @@ -109,9 +160,15 @@ const MCValue &Target) { if (Fixup.getKind() >= FirstLiteralRelocationKind) return true; - // TODO: Determine which relocation require special processing at linking - // time. - return false; + switch (Fixup.getTargetKind()) { + default: + return false; + case FK_Data_1: + case FK_Data_2: + case FK_Data_4: + case FK_Data_8: + return !Target.isAbsolute(); + } } bool LoongArchAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count, diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp @@ -263,6 +263,9 @@ case LoongArch::BCNEZ: FixupKind = LoongArch::fixup_loongarch_b21; break; + case LoongArch::B: + FixupKind = LoongArch::fixup_loongarch_b26; + break; } } diff --git a/llvm/test/MC/LoongArch/Relocations/fixups-diagnostics.s b/llvm/test/MC/LoongArch/Relocations/fixups-diagnostics.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/LoongArch/Relocations/fixups-diagnostics.s @@ -0,0 +1,29 @@ +# RUN: not llvm-mc --triple=loongarch64 --filetype=obj %s -o /dev/null 2>&1 | FileCheck %s + + beq $a0, $a1, far_distant # CHECK: :[[#@LINE]]:3: error: fixup value out of range + bne $a0, $a1, unaligned # CHECK: :[[#@LINE]]:3: error: fixup value must be 4-byte aligned + + bnez $a0, unaligned # CHECK: :[[#@LINE]]:3: error: fixup value must be 4-byte aligned + beqz $a0, far_distant_bz # CHECK: :[[#@LINE]]:3: error: fixup value out of range + + b unaligned # CHECK: :[[#@LINE]]:3: error: fixup value must be 4-byte aligned + + .byte 0 +unaligned: + .byte 0 + .byte 0 + .byte 0 + + .space 1<<16 +distant: + .space 1<<18 +far_distant: + + .byte 0 +unaligned_bz: + .byte 0 + .byte 0 + .byte 0 +distant_bz: + .space 1<<23 +far_distant_bz: diff --git a/llvm/test/MC/LoongArch/Relocations/fixups-invalid.s b/llvm/test/MC/LoongArch/Relocations/fixups-invalid.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/LoongArch/Relocations/fixups-invalid.s @@ -0,0 +1,7 @@ +# RUN: not llvm-mc --filetype=obj %s --triple=loongarch32 -o /dev/null 2>&1 \ +# RUN: | FileCheck %s +# RUN: not llvm-mc --filetype=obj %s --triple=loongarch64 -o /dev/null 2>&1 \ +# RUN: | FileCheck %s + +.byte foo # CHECK: [[#@LINE]]:7: error: 1-byte data relocations not supported +.2byte foo # CHECK: [[#@LINE]]:8: error: 2-byte data relocations not supported diff --git a/llvm/test/MC/LoongArch/Relocations/fixups.s b/llvm/test/MC/LoongArch/Relocations/fixups.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/LoongArch/Relocations/fixups.s @@ -0,0 +1,67 @@ +# RUN: llvm-mc --triple=loongarch64 %s --show-encoding \ +# RUN: | FileCheck --check-prefix=CHECK-FIXUP %s +# RUN: llvm-mc --filetype=obj --triple=loongarch64 %s \ +# RUN: | llvm-objdump -d - | FileCheck --check-prefix=CHECK-INSTR %s +# RUN: llvm-mc --filetype=obj --triple=loongarch64 %s \ +# RUN: | llvm-readobj -r - | FileCheck --check-prefix=CHECK-REL %s + +## Checks that fixups that can be resolved within the same object file are +## applied correctly. + +.LBB0: +lu12i.w $t1, %abs_hi20(val) +# CHECK-FIXUP: fixup A - offset: 0, value: %abs_hi20(val), kind: fixup_loongarch_abs_hi20 +# CHECK-INSTR: lu12i.w $t1, 74565 + +ori $t1, $t1, %abs_lo12(val) +# CHECK-FIXUP: fixup A - offset: 0, value: %abs_lo12(val), kind: fixup_loongarch_abs_lo12 +# CHECK-INSTR: ori $t1, $t1, 1656 + +b .LBB0 +# CHECK-FIXUP: fixup A - offset: 0, value: .LBB0, kind: fixup_loongarch_b26 +# CHECK-INSTR: b -8 +b .LBB2 +# CHECK-FIXUP: fixup A - offset: 0, value: .LBB2, kind: fixup_loongarch_b26 +# CHECK-INSTR: b 331004 +beq $a0, $a1, .LBB0 +# CHECK-FIXUP: fixup A - offset: 0, value: .LBB0, kind: fixup_loongarch_b16 +# CHECK-INSTR: beq $a0, $a1, -16 +blt $a0, $a1, .LBB1 +# CHECK-FIXUP: fixup A - offset: 0, value: .LBB1, kind: fixup_loongarch_b16 +# CHECK-INSTR: blt $a0, $a1, 1116 +beqz $a0, .LBB0 +# CHECK-FIXUP: fixup A - offset: 0, value: .LBB0, kind: fixup_loongarch_b21 +# CHECK-INSTR: beqz $a0, -24 +bnez $a0, .LBB1 +# CHECK-FIXUP: fixup A - offset: 0, value: .LBB1, kind: fixup_loongarch_b21 +# CHECK-INSTR: bnez $a0, 1108 + +.fill 1104 + +.LBB1: + +.fill 329876 +nop +.LBB2: + +.set val, 0x12345678 + +# CHECK-REL-NOT: R_LARCH + +## Testing the function call offset could resolved by assembler +## when the function and the callsite within the same compile unit. +func: +.fill 100 +bl func +# CHECK-FIXUP: fixup A - offset: 0, value: func, kind: fixup_loongarch_b26 +# CHECK-INSTR: bl -100 + +.fill 10000 +bl func +# CHECK-FIXUP: fixup A - offset: 0, value: func, kind: fixup_loongarch_b26 +# CHECK-INSTR: bl -10104 + +.fill 20888 +bl func +# CHECK-FIXUP: fixup A - offset: 0, value: func, kind: fixup_loongarch_b26 +# CHECK-INSTR: bl -30996