359 lines
13 KiB
C++
359 lines
13 KiB
C++
//===-- CSKYAsmBackend.cpp - CSKY Assembler Backend -----------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "CSKYAsmBackend.h"
|
|
#include "MCTargetDesc/CSKYMCTargetDesc.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/MC/MCAsmLayout.h"
|
|
#include "llvm/MC/MCAssembler.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCFixupKindInfo.h"
|
|
#include "llvm/MC/MCObjectWriter.h"
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
#define DEBUG_TYPE "csky-asmbackend"
|
|
|
|
using namespace llvm;
|
|
|
|
std::unique_ptr<MCObjectTargetWriter>
|
|
CSKYAsmBackend::createObjectTargetWriter() const {
|
|
return createCSKYELFObjectWriter();
|
|
}
|
|
|
|
const MCFixupKindInfo &
|
|
CSKYAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
|
|
|
|
static llvm::DenseMap<unsigned, MCFixupKindInfo> Infos = {
|
|
{CSKY::Fixups::fixup_csky_addr32, {"fixup_csky_addr32", 0, 32, 0}},
|
|
{CSKY::Fixups::fixup_csky_addr_hi16, {"fixup_csky_addr_hi16", 0, 32, 0}},
|
|
{CSKY::Fixups::fixup_csky_addr_lo16, {"fixup_csky_addr_lo16", 0, 32, 0}},
|
|
{CSKY::Fixups::fixup_csky_pcrel_imm16_scale2,
|
|
{"fixup_csky_pcrel_imm16_scale2", 0, 32, MCFixupKindInfo::FKF_IsPCRel}},
|
|
{CSKY::Fixups::fixup_csky_pcrel_uimm16_scale4,
|
|
{"fixup_csky_pcrel_uimm16_scale4", 0, 32,
|
|
MCFixupKindInfo::FKF_IsPCRel |
|
|
MCFixupKindInfo::FKF_IsAlignedDownTo32Bits}},
|
|
{CSKY::Fixups::fixup_csky_pcrel_uimm8_scale4,
|
|
{"fixup_csky_pcrel_uimm8_scale4", 0, 32,
|
|
MCFixupKindInfo::FKF_IsPCRel |
|
|
MCFixupKindInfo::FKF_IsAlignedDownTo32Bits}},
|
|
{CSKY::Fixups::fixup_csky_pcrel_imm26_scale2,
|
|
{"fixup_csky_pcrel_imm26_scale2", 0, 32, MCFixupKindInfo::FKF_IsPCRel}},
|
|
{CSKY::Fixups::fixup_csky_pcrel_imm18_scale2,
|
|
{"fixup_csky_pcrel_imm18_scale2", 0, 32, MCFixupKindInfo::FKF_IsPCRel}},
|
|
{CSKY::Fixups::fixup_csky_got32, {"fixup_csky_got32", 0, 32, 0}},
|
|
{CSKY::Fixups::fixup_csky_got_imm18_scale4,
|
|
{"fixup_csky_got_imm18_scale4", 0, 32, 0}},
|
|
{CSKY::Fixups::fixup_csky_gotoff, {"fixup_csky_gotoff", 0, 32, 0}},
|
|
{CSKY::Fixups::fixup_csky_gotpc,
|
|
{"fixup_csky_gotpc", 0, 32, MCFixupKindInfo::FKF_IsPCRel}},
|
|
{CSKY::Fixups::fixup_csky_plt32, {"fixup_csky_plt32", 0, 32, 0}},
|
|
{CSKY::Fixups::fixup_csky_plt_imm18_scale4,
|
|
{"fixup_csky_plt_imm18_scale4", 0, 32, 0}},
|
|
{CSKY::Fixups::fixup_csky_pcrel_imm10_scale2,
|
|
{"fixup_csky_pcrel_imm10_scale2", 0, 16, MCFixupKindInfo::FKF_IsPCRel}},
|
|
{CSKY::Fixups::fixup_csky_pcrel_uimm7_scale4,
|
|
{"fixup_csky_pcrel_uimm7_scale4", 0, 16,
|
|
MCFixupKindInfo::FKF_IsPCRel |
|
|
MCFixupKindInfo::FKF_IsAlignedDownTo32Bits}},
|
|
{CSKY::Fixups::fixup_csky_doffset_imm18,
|
|
{"fixup_csky_doffset_imm18", 0, 18, 0}},
|
|
{CSKY::Fixups::fixup_csky_doffset_imm18_scale2,
|
|
{"fixup_csky_doffset_imm18_scale2", 0, 18, 0}},
|
|
{CSKY::Fixups::fixup_csky_doffset_imm18_scale4,
|
|
{"fixup_csky_doffset_imm18_scale4", 0, 18, 0}}};
|
|
|
|
assert(Infos.size() == CSKY::NumTargetFixupKinds &&
|
|
"Not all fixup kinds added to Infos array");
|
|
|
|
if (FirstTargetFixupKind <= Kind && Kind < FirstLiteralRelocationKind) {
|
|
assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() &&
|
|
"Invalid kind!");
|
|
|
|
return Infos[Kind];
|
|
} else if (Kind < FirstTargetFixupKind) {
|
|
return MCAsmBackend::getFixupKindInfo(Kind);
|
|
} else {
|
|
return MCAsmBackend::getFixupKindInfo(FK_NONE);
|
|
}
|
|
}
|
|
|
|
static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
|
|
MCContext &Ctx) {
|
|
switch (Fixup.getTargetKind()) {
|
|
default:
|
|
llvm_unreachable("Unknown fixup kind!");
|
|
case CSKY::fixup_csky_got32:
|
|
case CSKY::fixup_csky_got_imm18_scale4:
|
|
case CSKY::fixup_csky_gotoff:
|
|
case CSKY::fixup_csky_gotpc:
|
|
case CSKY::fixup_csky_plt32:
|
|
case CSKY::fixup_csky_plt_imm18_scale4:
|
|
llvm_unreachable("Relocation should be unconditionally forced\n");
|
|
case FK_Data_1:
|
|
case FK_Data_2:
|
|
case FK_Data_4:
|
|
case FK_Data_8:
|
|
return Value;
|
|
case CSKY::fixup_csky_addr32:
|
|
return Value & 0xffffffff;
|
|
case CSKY::fixup_csky_pcrel_imm16_scale2:
|
|
if (!isIntN(17, Value))
|
|
Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value.");
|
|
if (Value & 0x1)
|
|
Ctx.reportError(Fixup.getLoc(), "fixup value must be 2-byte aligned.");
|
|
|
|
return (Value >> 1) & 0xffff;
|
|
case CSKY::fixup_csky_pcrel_uimm16_scale4:
|
|
if (!isUIntN(18, Value))
|
|
Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value.");
|
|
if (Value & 0x3)
|
|
Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned.");
|
|
|
|
return (Value >> 2) & 0xffff;
|
|
case CSKY::fixup_csky_pcrel_imm26_scale2:
|
|
if (!isIntN(27, Value))
|
|
Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value.");
|
|
if (Value & 0x1)
|
|
Ctx.reportError(Fixup.getLoc(), "fixup value must be 2-byte aligned.");
|
|
|
|
return (Value >> 1) & 0x3ffffff;
|
|
case CSKY::fixup_csky_pcrel_imm18_scale2:
|
|
if (!isIntN(19, Value))
|
|
Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value.");
|
|
if (Value & 0x1)
|
|
Ctx.reportError(Fixup.getLoc(), "fixup value must be 2-byte aligned.");
|
|
|
|
return (Value >> 1) & 0x3ffff;
|
|
case CSKY::fixup_csky_pcrel_uimm8_scale4: {
|
|
if (!isUIntN(10, Value))
|
|
Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value.");
|
|
if (Value & 0x3)
|
|
Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned.");
|
|
|
|
unsigned IMM4L = (Value >> 2) & 0xf;
|
|
unsigned IMM4H = (Value >> 6) & 0xf;
|
|
|
|
Value = (IMM4H << 21) | (IMM4L << 4);
|
|
return Value;
|
|
}
|
|
case CSKY::fixup_csky_pcrel_imm10_scale2:
|
|
if (!isIntN(11, Value))
|
|
Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value.");
|
|
if (Value & 0x1)
|
|
Ctx.reportError(Fixup.getLoc(), "fixup value must be 2-byte aligned.");
|
|
|
|
return (Value >> 1) & 0x3ff;
|
|
case CSKY::fixup_csky_pcrel_uimm7_scale4:
|
|
if ((Value >> 2) > 0xfe)
|
|
Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value.");
|
|
if (Value & 0x3)
|
|
Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned.");
|
|
|
|
if ((Value >> 2) <= 0x7f) {
|
|
unsigned IMM5L = (Value >> 2) & 0x1f;
|
|
unsigned IMM2H = (Value >> 7) & 0x3;
|
|
|
|
Value = (1 << 12) | (IMM2H << 8) | IMM5L;
|
|
} else {
|
|
unsigned IMM5L = (~Value >> 2) & 0x1f;
|
|
unsigned IMM2H = (~Value >> 7) & 0x3;
|
|
|
|
Value = (IMM2H << 8) | IMM5L;
|
|
}
|
|
|
|
return Value;
|
|
}
|
|
}
|
|
|
|
bool CSKYAsmBackend::fixupNeedsRelaxationAdvanced(const MCFixup &Fixup,
|
|
bool Resolved, uint64_t Value,
|
|
const MCRelaxableFragment *DF,
|
|
const MCAsmLayout &Layout,
|
|
const bool WasForced) const {
|
|
// Return true if the symbol is actually unresolved.
|
|
// Resolved could be always false when shouldForceRelocation return true.
|
|
// We use !WasForced to indicate that the symbol is unresolved and not forced
|
|
// by shouldForceRelocation.
|
|
if (!Resolved && !WasForced)
|
|
return true;
|
|
|
|
int64_t Offset = int64_t(Value);
|
|
switch (Fixup.getTargetKind()) {
|
|
default:
|
|
return false;
|
|
case CSKY::fixup_csky_pcrel_imm10_scale2:
|
|
return !isShiftedInt<10, 1>(Offset);
|
|
case CSKY::fixup_csky_pcrel_imm16_scale2:
|
|
return !isShiftedInt<16, 1>(Offset);
|
|
case CSKY::fixup_csky_pcrel_imm26_scale2:
|
|
return !isShiftedInt<26, 1>(Offset);
|
|
case CSKY::fixup_csky_pcrel_uimm7_scale4:
|
|
return ((Value >> 2) > 0xfe) || (Value & 0x3);
|
|
}
|
|
}
|
|
|
|
void CSKYAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup,
|
|
const MCValue &Target,
|
|
MutableArrayRef<char> Data, uint64_t Value,
|
|
bool IsResolved,
|
|
const MCSubtargetInfo *STI) const {
|
|
MCFixupKind Kind = Fixup.getKind();
|
|
if (Kind >= FirstLiteralRelocationKind)
|
|
return;
|
|
MCContext &Ctx = Asm.getContext();
|
|
MCFixupKindInfo Info = getFixupKindInfo(Kind);
|
|
if (!Value)
|
|
return; // Doesn't change encoding.
|
|
// Apply any target-specific value adjustments.
|
|
Value = adjustFixupValue(Fixup, Value, Ctx);
|
|
|
|
// Shift the value into position.
|
|
Value <<= Info.TargetOffset;
|
|
|
|
unsigned Offset = Fixup.getOffset();
|
|
unsigned NumBytes = alignTo(Info.TargetSize + Info.TargetOffset, 8) / 8;
|
|
|
|
assert(Offset + NumBytes <= Data.size() && "Invalid fixup offset!");
|
|
|
|
// For each byte of the fragment that the fixup touches, mask in the
|
|
// bits from the fixup value.
|
|
bool IsLittleEndian = (Endian == support::little);
|
|
bool IsInstFixup = (Kind >= FirstTargetFixupKind);
|
|
|
|
if (IsLittleEndian && IsInstFixup && (NumBytes == 4)) {
|
|
Data[Offset + 0] |= uint8_t((Value >> 16) & 0xff);
|
|
Data[Offset + 1] |= uint8_t((Value >> 24) & 0xff);
|
|
Data[Offset + 2] |= uint8_t(Value & 0xff);
|
|
Data[Offset + 3] |= uint8_t((Value >> 8) & 0xff);
|
|
} else {
|
|
for (unsigned I = 0; I != NumBytes; I++) {
|
|
unsigned Idx = IsLittleEndian ? I : (NumBytes - 1 - I);
|
|
Data[Offset + Idx] |= uint8_t((Value >> (I * 8)) & 0xff);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CSKYAsmBackend::mayNeedRelaxation(const MCInst &Inst,
|
|
const MCSubtargetInfo &STI) const {
|
|
switch (Inst.getOpcode()) {
|
|
default:
|
|
return false;
|
|
case CSKY::JBR32:
|
|
case CSKY::JBT32:
|
|
case CSKY::JBF32:
|
|
case CSKY::JBSR32:
|
|
if (!STI.getFeatureBits()[CSKY::Has2E3])
|
|
return false;
|
|
return true;
|
|
case CSKY::JBR16:
|
|
case CSKY::JBT16:
|
|
case CSKY::JBF16:
|
|
case CSKY::LRW16:
|
|
case CSKY::BR16:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool CSKYAsmBackend::shouldForceRelocation(const MCAssembler &Asm,
|
|
const MCFixup &Fixup,
|
|
const MCValue &Target) {
|
|
if (Fixup.getKind() >= FirstLiteralRelocationKind)
|
|
return true;
|
|
switch (Fixup.getTargetKind()) {
|
|
default:
|
|
break;
|
|
case CSKY::fixup_csky_got32:
|
|
case CSKY::fixup_csky_got_imm18_scale4:
|
|
case CSKY::fixup_csky_gotoff:
|
|
case CSKY::fixup_csky_gotpc:
|
|
case CSKY::fixup_csky_plt32:
|
|
case CSKY::fixup_csky_plt_imm18_scale4:
|
|
case CSKY::fixup_csky_doffset_imm18:
|
|
case CSKY::fixup_csky_doffset_imm18_scale2:
|
|
case CSKY::fixup_csky_doffset_imm18_scale4:
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CSKYAsmBackend::fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value,
|
|
const MCRelaxableFragment *DF,
|
|
const MCAsmLayout &Layout) const {
|
|
return false;
|
|
}
|
|
|
|
void CSKYAsmBackend::relaxInstruction(MCInst &Inst,
|
|
const MCSubtargetInfo &STI) const {
|
|
MCInst Res;
|
|
|
|
switch (Inst.getOpcode()) {
|
|
default:
|
|
LLVM_DEBUG(Inst.dump());
|
|
llvm_unreachable("Opcode not expected!");
|
|
case CSKY::LRW16:
|
|
Res.setOpcode(CSKY::LRW32);
|
|
Res.addOperand(Inst.getOperand(0));
|
|
Res.addOperand(Inst.getOperand(1));
|
|
break;
|
|
case CSKY::BR16:
|
|
Res.setOpcode(CSKY::BR32);
|
|
Res.addOperand(Inst.getOperand(0));
|
|
break;
|
|
case CSKY::JBSR32:
|
|
Res.setOpcode(CSKY::JSRI32);
|
|
Res.addOperand(Inst.getOperand(1));
|
|
break;
|
|
case CSKY::JBR32:
|
|
Res.setOpcode(CSKY::JMPI32);
|
|
Res.addOperand(Inst.getOperand(1));
|
|
break;
|
|
case CSKY::JBT32:
|
|
case CSKY::JBF32:
|
|
Res.setOpcode(Inst.getOpcode() == CSKY::JBT32 ? CSKY::JBT_E : CSKY::JBF_E);
|
|
Res.addOperand(Inst.getOperand(0));
|
|
Res.addOperand(Inst.getOperand(1));
|
|
Res.addOperand(Inst.getOperand(2));
|
|
break;
|
|
case CSKY::JBR16:
|
|
Res.setOpcode(CSKY::JBR32);
|
|
Res.addOperand(Inst.getOperand(0));
|
|
Res.addOperand(Inst.getOperand(1));
|
|
break;
|
|
case CSKY::JBT16:
|
|
case CSKY::JBF16:
|
|
// ck801
|
|
unsigned opcode;
|
|
if (STI.getFeatureBits()[CSKY::HasE2])
|
|
opcode = Inst.getOpcode() == CSKY::JBT16 ? CSKY::JBT32 : CSKY::JBF32;
|
|
else
|
|
opcode = Inst.getOpcode() == CSKY::JBT16 ? CSKY::JBT_E : CSKY::JBF_E;
|
|
|
|
Res.setOpcode(opcode);
|
|
Res.addOperand(Inst.getOperand(0));
|
|
Res.addOperand(Inst.getOperand(1));
|
|
Res.addOperand(Inst.getOperand(2));
|
|
break;
|
|
}
|
|
Inst = std::move(Res);
|
|
}
|
|
|
|
bool CSKYAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count,
|
|
const MCSubtargetInfo *STI) const {
|
|
OS.write_zeros(Count);
|
|
return true;
|
|
}
|
|
|
|
MCAsmBackend *llvm::createCSKYAsmBackend(const Target &T,
|
|
const MCSubtargetInfo &STI,
|
|
const MCRegisterInfo &MRI,
|
|
const MCTargetOptions &Options) {
|
|
return new CSKYAsmBackend(STI, Options);
|
|
}
|