Index: lib/Target/AVR/MCTargetDesc/AVRFixupKinds.h =================================================================== --- /dev/null +++ lib/Target/AVR/MCTargetDesc/AVRFixupKinds.h @@ -0,0 +1,149 @@ +//===-- AVRFixupKinds.h - AVR Specific Fixup Entries ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_AVR_FIXUP_KINDS_H +#define LLVM_AVR_FIXUP_KINDS_H + +#include "llvm/MC/MCFixup.h" + +namespace llvm { +namespace AVR { + +/// The set of supported fixups. +/// +/// Although most of the current fixup types reflect a unique relocation +/// one can have multiple fixup types for a given relocation and thus need +/// to be uniquely named. +/// +/// \note This table *must* be in the same order of +/// MCFixupKindInfo Infos[AVR::NumTargetFixupKinds] +/// in `AVRAsmBackend.cpp`. +enum Fixups { + /// A 32-bit AVR fixup. + fixup_32 = FirstTargetFixupKind, + + /// A 7-bit PC-relative fixup for the family of conditional + /// branches which take 7-bit targets (BRNE,BRGT,etc). + fixup_7_pcrel, + /// A 12-bit PC-relative fixup for the family of branches + /// which take 12-bit targets (RJMP,RCALL,etc). + /// \note Although the fixup is labelled as 13 bits, it + /// is actually only encoded in 12. The reason for + /// The nonmenclature is that AVR branch targets are + /// rightshifted by 1, because instructions are always + /// aligned to 2 bytes, so the 0'th bit is always 0. + /// This way there is 13-bits of precision. + fixup_13_pcrel, + + /// A 16-bit address. + fixup_16, + /// A 16-bit program memory address. + fixup_16_pm, + + /// Replaces the 8-bit immediate with another value. + fixup_ldi, + + /// Replaces the immediate operand of a 16-bit `Rd, K` instruction + /// with the lower 8 bits of a 16-bit value (bits 0-7). + fixup_lo8_ldi, + /// Replaces the immediate operand of a 16-bit `Rd, K` instruction + /// with the upper 8 bits of a 16-bit value (bits 8-15). + fixup_hi8_ldi, + /// Replaces the immediate operand of a 16-bit `Rd, K` instruction + /// with the upper 8 bits of a 24-bit value (bits 16-23). + fixup_hh8_ldi, + /// Replaces the immediate operand of a 16-bit `Rd, K` instruction + /// with the upper 8 bits of a 32-bit value (bits 24-31). + fixup_ms8_ldi, + + /// Replaces the immediate operand of a 16-bit `Rd, K` instruction + /// with the lower 8 bits of a negated 16-bit value (bits 0-7). + fixup_lo8_ldi_neg, + /// Replaces the immediate operand of a 16-bit `Rd, K` instruction + /// with the upper 8 bits of a negated 16-bit value (bits 8-15). + fixup_hi8_ldi_neg, + /// Replaces the immediate operand of a 16-bit `Rd, K` instruction + /// with the upper 8 bits of a negated negated 24-bit value (bits 16-23). + fixup_hh8_ldi_neg, + /// Replaces the immediate operand of a 16-bit `Rd, K` instruction + /// with the upper 8 bits of a negated negated 32-bit value (bits 24-31). + fixup_ms8_ldi_neg, + + /// Replaces the immediate operand of a 16-bit `Rd, K` instruction + /// with the lower 8 bits of a 16-bit program memory address value (bits 0-7). + fixup_lo8_ldi_pm, + /// Replaces the immediate operand of a 16-bit `Rd, K` instruction + /// with the upper 8 bits of a 16-bit program memory address value (bits + /// 8-15). + fixup_hi8_ldi_pm, + /// Replaces the immediate operand of a 16-bit `Rd, K` instruction + /// with the upper 8 bits of a 24-bit program memory address value (bits + /// 16-23). + fixup_hh8_ldi_pm, + + /// Replaces the immediate operand of a 16-bit `Rd, K` instruction + /// with the lower 8 bits of a negated 16-bit program memory address value + /// (bits 0-7). + fixup_lo8_ldi_pm_neg, + /// Replaces the immediate operand of a 16-bit `Rd, K` instruction + /// with the upper 8 bits of a negated 16-bit program memory address value + /// (bits 8-15). + fixup_hi8_ldi_pm_neg, + /// Replaces the immediate operand of a 16-bit `Rd, K` instruction + /// with the upper 8 bits of a negated 24-bit program memory address value + /// (bits 16-23). + fixup_hh8_ldi_pm_neg, + + /// A 22-bit fixup for the target of a `CALL k` or `JMP k` instruction. + fixup_call, + + fixup_6, + /// A symbol+addr fixup for the `LDD +, " family of instructions. + fixup_6_adiw, + + fixup_lo8_ldi_gs, + fixup_hi8_ldi_gs, + + fixup_8, + fixup_8_lo8, + fixup_8_hi8, + fixup_8_hlo8, + + /// Fixup to calculate the difference between two symbols. + /// Is the only stateful fixup. We do not support it yet. + fixup_sym_diff, + fixup_16_ldst, + + fixup_lds_sts_16, + + /// A 6-bit port address. + fixup_port6, + /// A 5-bit port address. + fixup_port5, + + // Marker + LastTargetFixupKind, + NumTargetFixupKinds = LastTargetFixupKind - FirstTargetFixupKind +}; + +namespace fixups { + +/// Adjusts the value of a branch target. +/// All branch targets in AVR are rightshifted by 1 to take advantage +/// of the fact that all instructions are aligned to addresses of size +/// 2, so bit 0 of an address is always 0. This gives us another bit +/// of precision. +/// \param[in,out] The target to adjust. +template inline void adjustBranchTarget(T &val) { val >>= 1; } + +} // end of namespace fixups +} +} // end of namespace llvm::AVR + +#endif // LLVM_AVR_FIXUP_KINDS_H Index: lib/Target/AVR/MCTargetDesc/AVRMCExpr.h =================================================================== --- /dev/null +++ lib/Target/AVR/MCTargetDesc/AVRMCExpr.h @@ -0,0 +1,88 @@ +//===-- AVRMCExpr.h - AVR specific MC expression classes --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_AVR_MCEXPR_H +#define LLVM_AVR_MCEXPR_H + +#include "llvm/MC/MCExpr.h" + +#include "MCTargetDesc/AVRFixupKinds.h" + +namespace llvm { + +/// A expression in AVR machine code. +class AVRMCExpr : public MCTargetExpr { +public: + /// Specifies the type of an expression. + enum VariantKind { + VK_AVR_None, + + VK_AVR_HI8, ///< Corresponds to `hi8()`. + VK_AVR_LO8, ///< Corresponds to `lo8()`. + VK_AVR_HH8, ///< Corresponds to `hlo8() and hh8()`. + VK_AVR_HHI8, ///< Corresponds to `hhi8()`. + + VK_AVR_PM_LO8, ///< Corresponds to `pm_lo8()`. + VK_AVR_PM_HI8, ///< Corresponds to `pm_hi8()`. + VK_AVR_PM_HH8 ///< Corresponds to `pm_hh8()`. + }; + +public: + /// Creates an AVR machine code expression. + static const AVRMCExpr *create(VariantKind Kind, const MCExpr *Expr, + bool isNegated, MCContext &Ctx); + + /// Gets the type of the expression. + VariantKind getKind() const { return Kind; } + /// Gets the name of the expression. + const char *getName() const; + const MCExpr *getSubExpr() const { return SubExpr; } + /// Gets the fixup which corresponds to the expression. + AVR::Fixups getFixupKind() const; + /// Evaluates the fixup as a constant value. + bool evaluateAsConstant(int64_t &Result) const; + + bool isNegated() const { return Negated; } + void setNegated(bool negated = true) { Negated = negated; } + + void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; + bool evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, + const MCFixup *Fixup) const override; + + void visitUsedExpr(MCStreamer &streamer) const override; + + MCFragment *findAssociatedFragment() const override { + return getSubExpr()->findAssociatedFragment(); + } + + void fixELFSymbolsInTLSFixups(MCAssembler &Asm) const override {} + + static bool classof(const MCExpr *E) { + return E->getKind() == MCExpr::Target; + } + +public: + static VariantKind getKindByName(StringRef Name); + +private: + int64_t evaluateAsInt64(int64_t Value) const; + + const VariantKind Kind; + const MCExpr *SubExpr; + bool Negated; + +private: + explicit AVRMCExpr(VariantKind Kind, const MCExpr *Expr, bool Negated) + : Kind(Kind), SubExpr(Expr), Negated(Negated) {} + ~AVRMCExpr() {} +}; + +} // end namespace llvm + +#endif // LLVM_AVR_MCEXPR_H Index: lib/Target/AVR/MCTargetDesc/AVRMCExpr.cpp =================================================================== --- /dev/null +++ lib/Target/AVR/MCTargetDesc/AVRMCExpr.cpp @@ -0,0 +1,189 @@ +//===-- AVRMCExpr.cpp - AVR specific MC expression classes ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AVRMCExpr.h" + +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCValue.h" +#include "llvm/MC/MCAsmLayout.h" + +namespace llvm { + +namespace { + +const struct ModifierEntry { + const char * const Spelling; + AVRMCExpr::VariantKind VariantKind; +} ModifierNames[] = { + {"lo8", AVRMCExpr::VK_AVR_LO8}, {"hi8", AVRMCExpr::VK_AVR_HI8}, + {"hh8", AVRMCExpr::VK_AVR_HH8}, // synonym with hlo8 + {"hlo8", AVRMCExpr::VK_AVR_HH8}, {"hhi8", AVRMCExpr::VK_AVR_HHI8}, + + {"pm_lo8", AVRMCExpr::VK_AVR_PM_LO8}, {"pm_hi8", AVRMCExpr::VK_AVR_PM_HI8}, + {"pm_hh8", AVRMCExpr::VK_AVR_PM_HH8}, +}; + +} // end of anonymous namespace + +const AVRMCExpr *AVRMCExpr::create(VariantKind Kind, const MCExpr *Expr, + bool Negated, MCContext &Ctx) { + return new (Ctx) AVRMCExpr(Kind, Expr, Negated); +} + +void AVRMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { + assert(Kind != VK_AVR_None); + + if (isNegated()) + OS << '-'; + + OS << getName() << '('; + getSubExpr()->print(OS, MAI); + OS << ')'; +} + +bool AVRMCExpr::evaluateAsConstant(int64_t &Result) const { + MCValue Value; + + bool isRelocatable = + getSubExpr()->evaluateAsRelocatable(Value, nullptr, nullptr); + + if (!isRelocatable) + return false; + + if (Value.isAbsolute()) { + Result = evaluateAsInt64(Value.getConstant()); + return true; + } + + return false; +} + +bool AVRMCExpr::evaluateAsRelocatableImpl(MCValue &Result, + const MCAsmLayout *Layout, + const MCFixup *Fixup) const { + MCValue Value; + bool isRelocatable = SubExpr->evaluateAsRelocatable(Value, Layout, Fixup); + + if (!isRelocatable) + return false; + + if (Value.isAbsolute()) { + Result = MCValue::get(evaluateAsInt64(Value.getConstant())); + } else { + if (!Layout) return false; + + MCContext &Context = Layout->getAssembler().getContext(); + const MCSymbolRefExpr *Sym = Value.getSymA(); + MCSymbolRefExpr::VariantKind Modifier = Sym->getKind(); + if (Modifier != MCSymbolRefExpr::VK_None) + return false; + + Sym = MCSymbolRefExpr::create(&Sym->getSymbol(), Modifier, Context); + Result = MCValue::get(Sym, Value.getSymB(), Value.getConstant()); + } + + return true; +} + +int64_t AVRMCExpr::evaluateAsInt64(int64_t Value) const { + if (Negated) + Value *= -1; + + switch (Kind) { + case AVRMCExpr::VK_AVR_LO8: + break; + case AVRMCExpr::VK_AVR_HI8: + Value >>= 8; + break; + case AVRMCExpr::VK_AVR_HH8: + Value >>= 16; + break; + case AVRMCExpr::VK_AVR_HHI8: + Value >>= 24; + break; + case AVRMCExpr::VK_AVR_PM_LO8: + Value >>= 1; + break; + case AVRMCExpr::VK_AVR_PM_HI8: + Value >>= 9; + break; + case AVRMCExpr::VK_AVR_PM_HH8: + Value >>= 17; + break; + + case AVRMCExpr::VK_AVR_None: + llvm_unreachable("Uninitialized expression."); + } + return static_cast(Value) & 0xff; +} + +AVR::Fixups AVRMCExpr::getFixupKind() const { + AVR::Fixups Kind = AVR::Fixups::LastTargetFixupKind; + + switch (getKind()) { + case VK_AVR_LO8: + Kind = isNegated() ? AVR::fixup_lo8_ldi_neg : AVR::fixup_lo8_ldi; + break; + case VK_AVR_HI8: + Kind = isNegated() ? AVR::fixup_hi8_ldi_neg : AVR::fixup_hi8_ldi; + break; + case VK_AVR_HH8: + Kind = isNegated() ? AVR::fixup_hh8_ldi_neg : AVR::fixup_hh8_ldi; + break; + case VK_AVR_HHI8: + Kind = isNegated() ? AVR::fixup_ms8_ldi_neg : AVR::fixup_ms8_ldi; + break; + + case VK_AVR_PM_LO8: + Kind = isNegated() ? AVR::fixup_lo8_ldi_pm_neg : AVR::fixup_lo8_ldi_pm; + break; + case VK_AVR_PM_HI8: + Kind = isNegated() ? AVR::fixup_hi8_ldi_pm_neg : AVR::fixup_hi8_ldi_pm; + break; + case VK_AVR_PM_HH8: + Kind = isNegated() ? AVR::fixup_hh8_ldi_pm_neg : AVR::fixup_hh8_ldi_pm; + break; + + case VK_AVR_None: + llvm_unreachable("Uninitialized expression"); + } + + return Kind; +} + +void AVRMCExpr::visitUsedExpr(MCStreamer &Streamer) const { + Streamer.visitUsedExpr(*getSubExpr()); +} + +const char *AVRMCExpr::getName() const { + const auto &Modifier = std::find_if( + std::begin(ModifierNames), std::end(ModifierNames), + [this](ModifierEntry const &Mod) { return Mod.VariantKind == Kind; }); + + if (Modifier != std::end(ModifierNames)) { + return Modifier->Spelling; + } + return nullptr; +} + +AVRMCExpr::VariantKind AVRMCExpr::getKindByName(StringRef Name) { + const auto &Modifier = std::find_if( + std::begin(ModifierNames), std::end(ModifierNames), + [&Name](ModifierEntry const &Mod) { return Mod.Spelling == Name; }); + + if (Modifier != std::end(ModifierNames)) { + return Modifier->VariantKind; + } + return VK_AVR_None; +} + +} // end of namespace llvm + Index: lib/Target/AVR/MCTargetDesc/CMakeLists.txt =================================================================== --- lib/Target/AVR/MCTargetDesc/CMakeLists.txt +++ lib/Target/AVR/MCTargetDesc/CMakeLists.txt @@ -1,6 +1,7 @@ add_llvm_library(LLVMAVRDesc AVRELFStreamer.cpp AVRMCAsmInfo.cpp + AVRMCExpr.cpp AVRTargetStreamer.cpp )