Index: llvm/trunk/include/llvm/MC/MCAsmBackend.h =================================================================== --- llvm/trunk/include/llvm/MC/MCAsmBackend.h +++ llvm/trunk/include/llvm/MC/MCAsmBackend.h @@ -96,6 +96,10 @@ const MCValue &Target, MutableArrayRef Data, uint64_t Value, bool IsResolved) const = 0; + /// Check whether the given target requires emitting differences of two + /// symbols as a set of relocations. + virtual bool requiresDiffExpressionRelocations() const { return false; } + /// @} /// \name Target Relaxation Interfaces Index: llvm/trunk/include/llvm/MC/MCFixup.h =================================================================== --- llvm/trunk/include/llvm/MC/MCFixup.h +++ llvm/trunk/include/llvm/MC/MCFixup.h @@ -41,6 +41,14 @@ FK_SecRel_2, ///< A two-byte section relative fixup. FK_SecRel_4, ///< A four-byte section relative fixup. FK_SecRel_8, ///< A eight-byte section relative fixup. + FK_Data_Add_1, ///< A one-byte add fixup. + FK_Data_Add_2, ///< A two-byte add fixup. + FK_Data_Add_4, ///< A four-byte add fixup. + FK_Data_Add_8, ///< A eight-byte add fixup. + FK_Data_Sub_1, ///< A one-byte sub fixup. + FK_Data_Sub_2, ///< A two-byte sub fixup. + FK_Data_Sub_4, ///< A four-byte sub fixup. + FK_Data_Sub_8, ///< A eight-byte sub fixup. FirstTargetFixupKind = 128, @@ -90,6 +98,28 @@ return FI; } + /// Return a fixup corresponding to the add half of a add/sub fixup pair for + /// the given Fixup. + static MCFixup createAddFor(const MCFixup &Fixup) { + MCFixup FI; + FI.Value = Fixup.getValue(); + FI.Offset = Fixup.getOffset(); + FI.Kind = (unsigned)getAddKindForKind(Fixup.getKind()); + FI.Loc = Fixup.getLoc(); + return FI; + } + + /// Return a fixup corresponding to the sub half of a add/sub fixup pair for + /// the given Fixup. + static MCFixup createSubFor(const MCFixup &Fixup) { + MCFixup FI; + FI.Value = Fixup.getValue(); + FI.Offset = Fixup.getOffset(); + FI.Kind = (unsigned)getSubKindForKind(Fixup.getKind()); + FI.Loc = Fixup.getLoc(); + return FI; + } + MCFixupKind getKind() const { return MCFixupKind(Kind); } uint32_t getOffset() const { return Offset; } @@ -109,6 +139,30 @@ } } + /// Return the generic fixup kind for an addition with a given size. It + /// is an error to pass an unsupported size. + static MCFixupKind getAddKindForKind(unsigned Kind) { + switch (Kind) { + default: llvm_unreachable("Unknown type to convert!"); + case FK_Data_1: return FK_Data_Add_1; + case FK_Data_2: return FK_Data_Add_2; + case FK_Data_4: return FK_Data_Add_4; + case FK_Data_8: return FK_Data_Add_8; + } + } + + /// Return the generic fixup kind for an subtraction with a given size. It + /// is an error to pass an unsupported size. + static MCFixupKind getSubKindForKind(unsigned Kind) { + switch (Kind) { + default: llvm_unreachable("Unknown type to convert!"); + case FK_Data_1: return FK_Data_Sub_1; + case FK_Data_2: return FK_Data_Sub_2; + case FK_Data_4: return FK_Data_Sub_4; + case FK_Data_8: return FK_Data_Sub_8; + } + } + SMLoc getLoc() const { return Loc; } }; Index: llvm/trunk/lib/MC/MCAsmBackend.cpp =================================================================== --- llvm/trunk/lib/MC/MCAsmBackend.cpp +++ llvm/trunk/lib/MC/MCAsmBackend.cpp @@ -84,7 +84,15 @@ {"FK_SecRel_1", 0, 8, 0}, {"FK_SecRel_2", 0, 16, 0}, {"FK_SecRel_4", 0, 32, 0}, - {"FK_SecRel_8", 0, 64, 0}}; + {"FK_SecRel_8", 0, 64, 0}, + {"FK_Data_Add_1", 0, 8, 0}, + {"FK_Data_Add_2", 0, 16, 0}, + {"FK_Data_Add_4", 0, 32, 0}, + {"FK_Data_Add_8", 0, 64, 0}, + {"FK_Data_Sub_1", 0, 8, 0}, + {"FK_Data_Sub_2", 0, 16, 0}, + {"FK_Data_Sub_4", 0, 32, 0}, + {"FK_Data_Sub_8", 0, 64, 0}}; assert((size_t)Kind <= array_lengthof(Builtins) && "Unknown fixup kind"); return Builtins[Kind]; Index: llvm/trunk/lib/MC/MCAssembler.cpp =================================================================== --- llvm/trunk/lib/MC/MCAssembler.cpp +++ llvm/trunk/lib/MC/MCAssembler.cpp @@ -720,7 +720,26 @@ // The fixup was unresolved, we need a relocation. Inform the object // writer of the relocation, and give it an opportunity to adjust the // fixup value if need be. - getWriter().recordRelocation(*this, Layout, &F, Fixup, Target, FixedValue); + if (Target.getSymA() && Target.getSymB() && + getBackend().requiresDiffExpressionRelocations()) { + // The fixup represents the difference between two symbols, which the + // backend has indicated must be resolved at link time. Split up the fixup + // into two relocations, one for the add, and one for the sub, and emit + // both of these. The constant will be associated with the add half of the + // expression. + MCFixup FixupAdd = MCFixup::createAddFor(Fixup); + MCValue TargetAdd = + MCValue::get(Target.getSymA(), nullptr, Target.getConstant()); + getWriter().recordRelocation(*this, Layout, &F, FixupAdd, TargetAdd, + FixedValue); + MCFixup FixupSub = MCFixup::createSubFor(Fixup); + MCValue TargetSub = MCValue::get(Target.getSymB()); + getWriter().recordRelocation(*this, Layout, &F, FixupSub, TargetSub, + FixedValue); + } else { + getWriter().recordRelocation(*this, Layout, &F, Fixup, Target, + FixedValue); + } } return std::make_tuple(Target, FixedValue, IsResolved); } Index: llvm/trunk/lib/MC/MCExpr.cpp =================================================================== --- llvm/trunk/lib/MC/MCExpr.cpp +++ llvm/trunk/lib/MC/MCExpr.cpp @@ -11,6 +11,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Config/llvm-config.h" +#include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCAsmLayout.h" #include "llvm/MC/MCAssembler.h" @@ -576,8 +577,12 @@ assert((!Layout || Asm) && "Must have an assembler object if layout is given!"); - // If we have a layout, we can fold resolved differences. - if (Asm) { + // If we have a layout, we can fold resolved differences. Do not do this if + // the backend requires this to be emitted as individual relocations, unless + // the InSet flag is set to get the current difference anyway (used for + // example to calculate symbol sizes). + if (Asm && + (InSet || !Asm->getBackend().requiresDiffExpressionRelocations())) { // First, fold out any differences which are fully resolved. By // reassociating terms in // Result = (LHS_A - LHS_B + LHS_Cst) + (RHS_A - RHS_B + RHS_Cst). Index: llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp =================================================================== --- llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp +++ llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp @@ -37,6 +37,11 @@ Is64Bit(Is64Bit) {} ~RISCVAsmBackend() override {} + // Generate diff expression relocations if the relax feature is enabled, + // otherwise it is safe for the assembler to calculate these internally. + bool requiresDiffExpressionRelocations() const override { + return STI.getFeatureBits()[RISCV::FeatureRelax]; + } void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, MutableArrayRef Data, uint64_t Value, bool IsResolved) const override; Index: llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp =================================================================== --- llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp +++ llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp @@ -56,6 +56,22 @@ return ELF::R_RISCV_32; case FK_Data_8: return ELF::R_RISCV_64; + case FK_Data_Add_1: + return ELF::R_RISCV_ADD8; + case FK_Data_Add_2: + return ELF::R_RISCV_ADD16; + case FK_Data_Add_4: + return ELF::R_RISCV_ADD32; + case FK_Data_Add_8: + return ELF::R_RISCV_ADD64; + case FK_Data_Sub_1: + return ELF::R_RISCV_SUB8; + case FK_Data_Sub_2: + return ELF::R_RISCV_SUB16; + case FK_Data_Sub_4: + return ELF::R_RISCV_SUB32; + case FK_Data_Sub_8: + return ELF::R_RISCV_SUB64; case RISCV::fixup_riscv_hi20: return ELF::R_RISCV_HI20; case RISCV::fixup_riscv_lo12_i: Index: llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp =================================================================== --- llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp +++ llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp @@ -44,7 +44,23 @@ bool RISCVMCExpr::evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, const MCFixup *Fixup) const { - return getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup); + if (!getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup)) + return false; + + // Some custom fixup types are not valid with symbol difference expressions + if (Res.getSymA() && Res.getSymB()) { + switch (getKind()) { + default: + return true; + case VK_RISCV_LO: + case VK_RISCV_HI: + case VK_RISCV_PCREL_LO: + case VK_RISCV_PCREL_HI: + return false; + } + } + + return true; } void RISCVMCExpr::visitUsedExpr(MCStreamer &Streamer) const { Index: llvm/trunk/test/MC/RISCV/fixups-expr.s =================================================================== --- llvm/trunk/test/MC/RISCV/fixups-expr.s +++ llvm/trunk/test/MC/RISCV/fixups-expr.s @@ -0,0 +1,47 @@ +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax %s \ +# RUN: | llvm-readobj -r | FileCheck -check-prefix=RELAX %s +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=-relax %s \ +# RUN: | llvm-readobj -r | FileCheck -check-prefix=NORELAX %s + +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax %s \ +# RUN: | llvm-readobj -r | FileCheck -check-prefix=RELAX %s +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=-relax %s \ +# RUN: | llvm-readobj -r | FileCheck -check-prefix=NORELAX %s + +# Check that subtraction expressions are emitted as two relocations +# only when relaxation is enabled + +.globl G1 +.globl G2 +.L1: +G1: +addi a0, a0, 0 +.L2: +G2: + +.data +.dword .L2-.L1 +.dword G2-G1 +.word .L2-.L1 +.word G2-G1 +.half .L2-.L1 +.half G2-G1 +.byte .L2-.L1 +.byte G2-G1 +# RELAX: 0x0 R_RISCV_ADD64 .L2 0x0 +# RELAX: 0x0 R_RISCV_SUB64 .L1 0x0 +# RELAX: 0x8 R_RISCV_ADD64 G2 0x0 +# RELAX: 0x8 R_RISCV_SUB64 G1 0x0 +# RELAX: 0x10 R_RISCV_ADD32 .L2 0x0 +# RELAX: 0x10 R_RISCV_SUB32 .L1 0x0 +# RELAX: 0x14 R_RISCV_ADD32 G2 0x0 +# RELAX: 0x14 R_RISCV_SUB32 G1 0x0 +# RELAX: 0x18 R_RISCV_ADD16 .L2 0x0 +# RELAX: 0x18 R_RISCV_SUB16 .L1 0x0 +# RELAX: 0x1A R_RISCV_ADD16 G2 0x0 +# RELAX: 0x1A R_RISCV_SUB16 G1 0x0 +# RELAX: 0x1C R_RISCV_ADD8 .L2 0x0 +# RELAX: 0x1C R_RISCV_SUB8 .L1 0x0 +# RELAX: 0x1D R_RISCV_ADD8 G2 0x0 +# RELAX: 0x1D R_RISCV_SUB8 G1 0x0 +# NORELAX-NOT: R_RISCV Index: llvm/trunk/test/MC/RISCV/hilo-constaddr-expr.s =================================================================== --- llvm/trunk/test/MC/RISCV/hilo-constaddr-expr.s +++ llvm/trunk/test/MC/RISCV/hilo-constaddr-expr.s @@ -0,0 +1,31 @@ +# RUN: not llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax %s 2>&1 \ +# RUN: | FileCheck %s -check-prefix=CHECK-RELAX +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=-relax %s 2>&1 \ +# RUN: | llvm-objdump -d - | FileCheck %s -check-prefix=CHECK-INSTR +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=-relax %s 2>&1 \ +# RUN: | llvm-objdump -r - | FileCheck %s -check-prefix=CHECK-REL + +# Check the assembler rejects hi and lo expressions with constant expressions +# involving labels when diff expressions are emitted as relocation pairs. +# Test case derived from test/MC/Mips/hilo-addressing.s + +tmp1: + # Emit zeros so that difference between tmp1 and tmp3 is 0x30124 bytes. + .fill 0x30124-8 +tmp2: +# CHECK-RELAX: :[[@LINE+1]]:{{[0-9]+}}: error: expected relocatable expression + lui t0, %hi(tmp3-tmp1) +# CHECK-RELAX: :[[@LINE+1]]:{{[0-9]+}}: error: expected relocatable expression + lw ra, %lo(tmp3-tmp1)(t0) +# CHECK-INSTR: lui t0, 48 +# CHECK-INSTR: lw ra, 292(t0) + +tmp3: +# CHECK-RELAX: :[[@LINE+1]]:{{[0-9]+}}: error: expected relocatable expression + lui t1, %hi(tmp2-tmp3) +# CHECK-RELAX: :[[@LINE+1]]:{{[0-9]+}}: error: expected relocatable expression + lw sp, %lo(tmp2-tmp3)(t1) +# CHECK-INSTR: lui t1, 0 +# CHECK-INSTR: lw sp, -8(t1) + +# CHECK-REL-NOT: R_RISCV \ No newline at end of file Index: llvm/trunk/test/MC/RISCV/hilo-constaddr.s =================================================================== --- llvm/trunk/test/MC/RISCV/hilo-constaddr.s +++ llvm/trunk/test/MC/RISCV/hilo-constaddr.s @@ -4,9 +4,8 @@ # RUN: llvm-mc -filetype=obj -triple=riscv32 %s \ # RUN: | llvm-readobj -r | FileCheck %s -check-prefix=CHECK-REL -# Check the assembler can handle hi and lo expressions with a constant -# address, and constant expressions involving labels. Test case derived from -# test/MC/Mips/hilo-addressing.s +# Check the assembler can handle hi and lo expressions with a constant +# address. Test case derived from test/MC/Mips/hilo-addressing.s # Check that 1 is added to the high 20 bits if bit 11 of the low part is 1. .equ addr, 0xdeadbeef @@ -15,25 +14,4 @@ # CHECK-INSTR: lui t0, 912092 # CHECK-INSTR: lw ra, -273(t0) -# Check that assembler can handle %hi(label1 - label2) and %lo(label1 - label2) -# expressions. - -tmp1: - # Emit zeros so that difference between tmp1 and tmp3 is 0x30124 bytes. - .fill 0x30124-8 -tmp2: - lui t0, %hi(tmp3-tmp1) - lw ra, %lo(tmp3-tmp1)(t0) -# CHECK-INSTR: lui t0, 48 -# CHECK-INSTR: lw ra, 292(t0) - -tmp3: - lui t1, %hi(tmp2-tmp3) - lw sp, %lo(tmp2-tmp3)(t1) -# CHECK-INSTR: lui t1, 0 -# CHECK-INSTR: lw sp, -8(t1) - -# Check that a relocation isn't emitted for %hi(label1 - label2) and -# %lo(label1 - label2) expressions. - # CHECK-REL-NOT: R_RISCV