diff options
Diffstat (limited to 'contrib/llvm/lib/Target/AVR')
39 files changed, 8705 insertions, 598 deletions
diff --git a/contrib/llvm/lib/Target/AVR/AVR.h b/contrib/llvm/lib/Target/AVR/AVR.h index 041c77c..8e5cc53 100644 --- a/contrib/llvm/lib/Target/AVR/AVR.h +++ b/contrib/llvm/lib/Target/AVR/AVR.h @@ -27,9 +27,15 @@ FunctionPass *createAVRISelDag(AVRTargetMachine &TM, CodeGenOpt::Level OptLevel); FunctionPass *createAVRExpandPseudoPass(); FunctionPass *createAVRFrameAnalyzerPass(); +FunctionPass *createAVRInstrumentFunctionsPass(); +FunctionPass *createAVRRelaxMemPass(); FunctionPass *createAVRDynAllocaSRPass(); FunctionPass *createAVRBranchSelectionPass(); +void initializeAVRExpandPseudoPass(PassRegistry&); +void initializeAVRInstrumentFunctionsPass(PassRegistry&); +void initializeAVRRelaxMemPass(PassRegistry&); + /// Contains the AVR backend. namespace AVR { diff --git a/contrib/llvm/lib/Target/AVR/AVR.td b/contrib/llvm/lib/Target/AVR/AVR.td index 27cf212..d03b983 100644 --- a/contrib/llvm/lib/Target/AVR/AVR.td +++ b/contrib/llvm/lib/Target/AVR/AVR.td @@ -16,493 +16,10 @@ include "llvm/Target/Target.td" //===---------------------------------------------------------------------===// -// AVR Subtarget Features. +// AVR Device Definitions //===---------------------------------------------------------------------===// -// :TODO: Implement the skip errata, see `gcc/config/avr/avr-arch.h` for details -// :TODO: We define all devices with SRAM to have all variants of LD/ST/LDD/STD. -// In reality, avr1 (no SRAM) has one variant each of `LD` and `ST`. -// avr2 (with SRAM) adds the rest of the variants. -// :TODO: s/AVRTiny/Tiny - - -// A feature set aggregates features, grouping them. We don't want to create a -// new member in AVRSubtarget (to store a value) for each set because we do not -// care if the set is supported, only the subfeatures inside the set. We fix -// this by simply setting the same dummy member for all feature sets, which is -// then ignored. -class FeatureSet<string name, string desc, list<SubtargetFeature> i> - : SubtargetFeature<name, "m_FeatureSetDummy", "true", desc, i>; - -// A family of microcontrollers, defining a set of supported features. -class Family<string name, list<SubtargetFeature> i> - : FeatureSet<name, !strconcat("The device is a part of the ", - name, " family"), i>; - -// The device has SRAM, and supports the bare minimum of -// SRAM-relevant instructions. -// -// These are: -// LD - all 9 variants -// ST - all 9 variants -// LDD - two variants for Y and Z -// STD - two variants for Y and Z -// `LDS Rd, K` -// `STS k, Rr` -// `PUSH`/`POP` -def FeatureSRAM : SubtargetFeature<"sram", "m_hasSRAM", "true", - "The device has random access memory">; - -// The device supports the `JMP k` and `CALL k` instructions. -def FeatureJMPCALL : SubtargetFeature<"jmpcall", "m_hasJMPCALL", "true", - "The device supports the `JMP` and " - "`CALL` instructions">; - - -// The device supports the indirect branches `IJMP` and `ICALL`. -def FeatureIJMPCALL : SubtargetFeature<"ijmpcall", "m_hasIJMPCALL", - "true", - "The device supports `IJMP`/`ICALL`" - "instructions">; - -// The device supports the extended indirect branches `EIJMP` and `EICALL`. -def FeatureEIJMPCALL : SubtargetFeature<"eijmpcall", "m_hasEIJMPCALL", - "true", "The device supports the " - "`EIJMP`/`EICALL` instructions">; - -// The device supports `ADDI Rd, K`, `SUBI Rd, K`. -def FeatureADDSUBIW : SubtargetFeature<"addsubiw", "m_hasADDSUBIW", - "true", "Enable 16-bit register-immediate " - "addition and subtraction instructions">; - -// The device has an 8-bit stack pointer (SP) register. -def FeatureSmallStack : SubtargetFeature<"smallstack", "m_hasSmallStack", - "true", "The device has an 8-bit " - "stack pointer">; - -// The device supports the 16-bit GPR pair MOVW instruction. -def FeatureMOVW : SubtargetFeature<"movw", "m_hasMOVW", "true", - "The device supports the 16-bit MOVW " - "instruction">; - -// The device supports the `LPM` instruction, with implied destination being r0. -def FeatureLPM : SubtargetFeature<"lpm", "m_hasLPM", "true", - "The device supports the `LPM` instruction">; - -// The device supports the `LPM Rd, Z[+] instruction. -def FeatureLPMX : SubtargetFeature<"lpmx", "m_hasLPMX", "true", - "The device supports the `LPM Rd, Z[+]` " - "instruction">; - -// The device supports the `ELPM` instruction. -def FeatureELPM : SubtargetFeature<"elpm", "m_hasELPM", "true", - "The device supports the ELPM instruction">; - -// The device supports the `ELPM Rd, Z[+]` instructions. -def FeatureELPMX : SubtargetFeature<"elpmx", "m_hasELPMX", "true", - "The device supports the `ELPM Rd, Z[+]` " - "instructions">; - -// The device supports the `SPM` instruction. -def FeatureSPM : SubtargetFeature<"spm", "m_hasSPM", "true", - "The device supports the `SPM` instruction">; - -// The device supports the `SPM Z+` instruction. -def FeatureSPMX : SubtargetFeature<"spmx", "m_hasSPMX", "true", - "The device supports the `SPM Z+` " - "instruction">; - -// The device supports the `DES k` instruction. -def FeatureDES : SubtargetFeature<"des", "m_hasDES", "true", - "The device supports the `DES k` encryption " - "instruction">; - -// The device supports the Read-Write-Modify instructions -// XCH, LAS, LAC, and LAT. -def FeatureRMW : SubtargetFeature<"rmw", "m_supportsRMW", "true", - "The device supports the read-write-modify " - "instructions: XCH, LAS, LAC, LAT">; - -// The device supports the `[F]MUL[S][U]` family of instructions. -def FeatureMultiplication : SubtargetFeature<"mul", "m_supportsMultiplication", - "true", "The device supports the " - "multiplication instructions">; - -// The device supports the `BREAK` instruction. -def FeatureBREAK : SubtargetFeature<"break", "m_hasBREAK", "true", - "The device supports the `BREAK` debugging " - "instruction">; - -// The device has instruction encodings specific to the Tiny core. -def FeatureTinyEncoding : SubtargetFeature<"tinyencoding", - "m_hasTinyEncoding", "true", - "The device has Tiny core specific " - "instruction encodings">; - -class ELFArch<string name> : SubtargetFeature<"", "ELFArch", - !strconcat("ELF::",name), "">; - -// ELF e_flags architecture values -def ELFArchAVR1 : ELFArch<"EF_AVR_ARCH_AVR1">; -def ELFArchAVR2 : ELFArch<"EF_AVR_ARCH_AVR2">; -def ELFArchAVR25 : ELFArch<"EF_AVR_ARCH_AVR25">; -def ELFArchAVR3 : ELFArch<"EF_AVR_ARCH_AVR3">; -def ELFArchAVR31 : ELFArch<"EF_AVR_ARCH_AVR31">; -def ELFArchAVR35 : ELFArch<"EF_AVR_ARCH_AVR35">; -def ELFArchAVR4 : ELFArch<"EF_AVR_ARCH_AVR4">; -def ELFArchAVR5 : ELFArch<"EF_AVR_ARCH_AVR5">; -def ELFArchAVR51 : ELFArch<"EF_AVR_ARCH_AVR51">; -def ELFArchAVR6 : ELFArch<"EF_AVR_ARCH_AVR6">; -def ELFArchAVRTiny : ELFArch<"EF_AVR_ARCH_AVRTINY">; -def ELFArchXMEGA1 : ELFArch<"EF_AVR_ARCH_XMEGA1">; -def ELFArchXMEGA2 : ELFArch<"EF_AVR_ARCH_XMEGA2">; -def ELFArchXMEGA3 : ELFArch<"EF_AVR_ARCH_XMEGA3">; -def ELFArchXMEGA4 : ELFArch<"EF_AVR_ARCH_XMEGA4">; -def ELFArchXMEGA5 : ELFArch<"EF_AVR_ARCH_XMEGA5">; -def ELFArchXMEGA6 : ELFArch<"EF_AVR_ARCH_XMEGA6">; -def ELFArchXMEGA7 : ELFArch<"EF_AVR_ARCH_XMEGA7">; - -//===---------------------------------------------------------------------===// -// AVR Families -//===---------------------------------------------------------------------===// - -// The device has at least the bare minimum that **every** single AVR -// device should have. -def FamilyAVR0 : Family<"avr0", []>; - -def FamilyAVR1 : Family<"avr1", [FamilyAVR0, FeatureLPM]>; - -def FamilyAVR2 : Family<"avr2", - [FamilyAVR1, FeatureIJMPCALL, FeatureADDSUBIW, - FeatureSRAM]>; - -def FamilyAVR25 : Family<"avr25", - [FamilyAVR2, FeatureMOVW, FeatureLPMX, - FeatureSPM, FeatureBREAK]>; - -def FamilyAVR3 : Family<"avr3", - [FamilyAVR2, FeatureJMPCALL]>; - -def FamilyAVR31 : Family<"avr31", - [FamilyAVR3, FeatureELPM]>; - -def FamilyAVR35 : Family<"avr35", - [FamilyAVR3, FeatureMOVW, FeatureLPMX, - FeatureSPM, FeatureBREAK]>; - -def FamilyAVR4 : Family<"avr4", - [FamilyAVR2, FeatureMultiplication, - FeatureMOVW, FeatureLPMX, FeatureSPM, - FeatureBREAK]>; - -def FamilyAVR5 : Family<"avr5", - [FamilyAVR3, FeatureMultiplication, - FeatureMOVW, FeatureLPMX, FeatureSPM, - FeatureBREAK]>; - -def FamilyAVR51 : Family<"avr51", - [FamilyAVR5, FeatureELPM, FeatureELPMX]>; - -def FamilyAVR6 : Family<"avr6", - [FamilyAVR51]>; - -def FamilyAVRTiny : Family<"avrtiny", - [FamilyAVR0, FeatureBREAK, FeatureSRAM, - FeatureTinyEncoding]>; - -def FamilyXMEGA : Family<"xmega", - [FamilyAVR51, FeatureEIJMPCALL, FeatureSPMX, - FeatureDES]>; - -def FamilyXMEGAU : Family<"xmegau", - [FamilyXMEGA, FeatureRMW]>; - -def FeatureSetSpecial : FeatureSet<"special", - "Enable use of the entire instruction " - "set - used for debugging", - [FeatureSRAM, FeatureJMPCALL, - FeatureIJMPCALL, FeatureEIJMPCALL, - FeatureADDSUBIW, FeatureMOVW, - FeatureLPM, FeatureLPMX, FeatureELPM, - FeatureELPMX, FeatureSPM, FeatureSPMX, - FeatureDES, FeatureRMW, - FeatureMultiplication, FeatureBREAK]>; - -//===---------------------------------------------------------------------===// -// AVR microcontrollers supported. -//===---------------------------------------------------------------------===// - -class Device<string Name, Family Fam, ELFArch Arch, - list<SubtargetFeature> ExtraFeatures = []> - : Processor<Name, NoItineraries, !listconcat([Fam,Arch],ExtraFeatures)>; - -// Generic MCUs -// Note that several versions of GCC has strange ELF architecture -// settings for backwards compatibility - see `gas/config/tc-avr.c` -// in AVR binutils. We do not replicate this. -def : Device<"avr1", FamilyAVR1, ELFArchAVR1>; -def : Device<"avr2", FamilyAVR2, ELFArchAVR2>; -def : Device<"avr25", FamilyAVR25, ELFArchAVR25>; -def : Device<"avr3", FamilyAVR3, ELFArchAVR3>; -def : Device<"avr31", FamilyAVR31, ELFArchAVR31>; -def : Device<"avr35", FamilyAVR35, ELFArchAVR35>; -def : Device<"avr4", FamilyAVR4, ELFArchAVR4>; -def : Device<"avr5", FamilyAVR5, ELFArchAVR5>; -def : Device<"avr51", FamilyAVR51, ELFArchAVR51>; -def : Device<"avr6", FamilyAVR6, ELFArchAVR6>; -def : Device<"avrxmega1", FamilyXMEGA, ELFArchXMEGA1>; -def : Device<"avrxmega2", FamilyXMEGA, ELFArchXMEGA2>; -def : Device<"avrxmega3", FamilyXMEGA, ELFArchXMEGA3>; -def : Device<"avrxmega4", FamilyXMEGA, ELFArchXMEGA4>; -def : Device<"avrxmega5", FamilyXMEGA, ELFArchXMEGA5>; -def : Device<"avrxmega6", FamilyXMEGA, ELFArchXMEGA6>; -def : Device<"avrxmega7", FamilyXMEGA, ELFArchXMEGA7>; -def : Device<"avrtiny", FamilyAVRTiny, ELFArchAVRTiny>; - -// Specific MCUs -def : Device<"at90s1200", FamilyAVR0, ELFArchAVR1>; -def : Device<"attiny11", FamilyAVR1, ELFArchAVR1>; -def : Device<"attiny12", FamilyAVR1, ELFArchAVR1>; -def : Device<"attiny15", FamilyAVR1, ELFArchAVR1>; -def : Device<"attiny28", FamilyAVR1, ELFArchAVR1>; -def : Device<"at90s2313", FamilyAVR2, ELFArchAVR2>; -def : Device<"at90s2323", FamilyAVR2, ELFArchAVR2>; -def : Device<"at90s2333", FamilyAVR2, ELFArchAVR2>; -def : Device<"at90s2343", FamilyAVR2, ELFArchAVR2>; -def : Device<"attiny22", FamilyAVR2, ELFArchAVR2>; -def : Device<"attiny26", FamilyAVR2, ELFArchAVR2, [FeatureLPMX]>; -def : Device<"at86rf401", FamilyAVR2, ELFArchAVR25, - [FeatureMOVW, FeatureLPMX]>; -def : Device<"at90s4414", FamilyAVR2, ELFArchAVR2>; -def : Device<"at90s4433", FamilyAVR2, ELFArchAVR2>; -def : Device<"at90s4434", FamilyAVR2, ELFArchAVR2>; -def : Device<"at90s8515", FamilyAVR2, ELFArchAVR2>; -def : Device<"at90c8534", FamilyAVR2, ELFArchAVR2>; -def : Device<"at90s8535", FamilyAVR2, ELFArchAVR2>; -def : Device<"ata5272", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny13", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny13a", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny2313", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny2313a", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny24", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny24a", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny4313", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny44", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny44a", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny84", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny84a", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny25", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny45", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny85", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny261", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny261a", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny461", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny461a", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny861", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny861a", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny87", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny43u", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny48", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny88", FamilyAVR25, ELFArchAVR25>; -def : Device<"attiny828", FamilyAVR25, ELFArchAVR25>; -def : Device<"at43usb355", FamilyAVR3, ELFArchAVR3>; -def : Device<"at76c711", FamilyAVR3, ELFArchAVR3>; -def : Device<"atmega103", FamilyAVR31, ELFArchAVR31>; -def : Device<"at43usb320", FamilyAVR31, ELFArchAVR31>; -def : Device<"attiny167", FamilyAVR35, ELFArchAVR35>; -def : Device<"at90usb82", FamilyAVR35, ELFArchAVR35>; -def : Device<"at90usb162", FamilyAVR35, ELFArchAVR35>; -def : Device<"ata5505", FamilyAVR35, ELFArchAVR35>; -def : Device<"atmega8u2", FamilyAVR35, ELFArchAVR35>; -def : Device<"atmega16u2", FamilyAVR35, ELFArchAVR35>; -def : Device<"atmega32u2", FamilyAVR35, ELFArchAVR35>; -def : Device<"attiny1634", FamilyAVR35, ELFArchAVR35>; -def : Device<"atmega8", FamilyAVR4, ELFArchAVR4>; // FIXME: family may be wrong -def : Device<"ata6289", FamilyAVR4, ELFArchAVR4>; -def : Device<"atmega8a", FamilyAVR4, ELFArchAVR4>; -def : Device<"ata6285", FamilyAVR4, ELFArchAVR4>; -def : Device<"ata6286", FamilyAVR4, ELFArchAVR4>; -def : Device<"atmega48", FamilyAVR4, ELFArchAVR4>; -def : Device<"atmega48a", FamilyAVR4, ELFArchAVR4>; -def : Device<"atmega48pa", FamilyAVR4, ELFArchAVR4>; -def : Device<"atmega48p", FamilyAVR4, ELFArchAVR4>; -def : Device<"atmega88", FamilyAVR4, ELFArchAVR4>; -def : Device<"atmega88a", FamilyAVR4, ELFArchAVR4>; -def : Device<"atmega88p", FamilyAVR4, ELFArchAVR4>; -def : Device<"atmega88pa", FamilyAVR4, ELFArchAVR4>; -def : Device<"atmega8515", FamilyAVR2, ELFArchAVR4, - [FeatureMultiplication, FeatureMOVW, FeatureLPMX, FeatureSPM]>; -def : Device<"atmega8535", FamilyAVR2, ELFArchAVR4, - [FeatureMultiplication, FeatureMOVW, FeatureLPMX, FeatureSPM]>; -def : Device<"atmega8hva", FamilyAVR4, ELFArchAVR4>; -def : Device<"at90pwm1", FamilyAVR4, ELFArchAVR4>; -def : Device<"at90pwm2", FamilyAVR4, ELFArchAVR4>; -def : Device<"at90pwm2b", FamilyAVR4, ELFArchAVR4>; -def : Device<"at90pwm3", FamilyAVR4, ELFArchAVR4>; -def : Device<"at90pwm3b", FamilyAVR4, ELFArchAVR4>; -def : Device<"at90pwm81", FamilyAVR4, ELFArchAVR4>; -def : Device<"ata5790", FamilyAVR5, ELFArchAVR5>; -def : Device<"ata5795", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega16", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega16a", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega161", FamilyAVR3, ELFArchAVR5, - [FeatureMultiplication, FeatureMOVW, FeatureLPMX, FeatureSPM]>; -def : Device<"atmega162", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega163", FamilyAVR3, ELFArchAVR5, - [FeatureMultiplication, FeatureMOVW, FeatureLPMX, FeatureSPM]>; -def : Device<"atmega164a", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega164p", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega164pa", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega165", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega165a", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega165p", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega165pa", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega168", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega168a", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega168p", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega168pa", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega169", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega169a", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega169p", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega169pa", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega32", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega32a", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega323", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega324a", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega324p", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega324pa", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega325", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega325a", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega325p", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega325pa", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega3250", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega3250a", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega3250p", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega3250pa", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega328", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega328p", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega329", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega329a", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega329p", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega329pa", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega3290", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega3290a", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega3290p", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega3290pa", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega406", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega64", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega64a", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega640", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega644", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega644a", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega644p", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega644pa", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega645", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega645a", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega645p", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega649", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega649a", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega649p", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega6450", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega6450a", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega6450p", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega6490", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega6490a", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega6490p", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega64rfr2", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega644rfr2", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega16hva", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega16hva2", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega16hvb", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega16hvbrevb", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega32hvb", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega32hvbrevb", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega64hve", FamilyAVR5, ELFArchAVR5>; -def : Device<"at90can32", FamilyAVR5, ELFArchAVR5>; -def : Device<"at90can64", FamilyAVR5, ELFArchAVR5>; -def : Device<"at90pwm161", FamilyAVR5, ELFArchAVR5>; -def : Device<"at90pwm216", FamilyAVR5, ELFArchAVR5>; -def : Device<"at90pwm316", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega32c1", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega64c1", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega16m1", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega32m1", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega64m1", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega16u4", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega32u4", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega32u6", FamilyAVR5, ELFArchAVR5>; -def : Device<"at90usb646", FamilyAVR5, ELFArchAVR5>; -def : Device<"at90usb647", FamilyAVR5, ELFArchAVR5>; -def : Device<"at90scr100", FamilyAVR5, ELFArchAVR5>; -def : Device<"at94k", FamilyAVR3, ELFArchAVR5, - [FeatureMultiplication, FeatureMOVW, FeatureLPMX]>; -def : Device<"m3000", FamilyAVR5, ELFArchAVR5>; -def : Device<"atmega128", FamilyAVR51, ELFArchAVR51>; -def : Device<"atmega128a", FamilyAVR51, ELFArchAVR51>; -def : Device<"atmega1280", FamilyAVR51, ELFArchAVR51>; -def : Device<"atmega1281", FamilyAVR51, ELFArchAVR51>; -def : Device<"atmega1284", FamilyAVR51, ELFArchAVR51>; -def : Device<"atmega1284p", FamilyAVR51, ELFArchAVR51>; -def : Device<"atmega128rfa1", FamilyAVR51, ELFArchAVR51>; -def : Device<"atmega128rfr2", FamilyAVR51, ELFArchAVR51>; -def : Device<"atmega1284rfr2", FamilyAVR51, ELFArchAVR51>; -def : Device<"at90can128", FamilyAVR51, ELFArchAVR51>; -def : Device<"at90usb1286", FamilyAVR51, ELFArchAVR51>; -def : Device<"at90usb1287", FamilyAVR51, ELFArchAVR51>; -def : Device<"atmega2560", FamilyAVR6, ELFArchAVR6>; -def : Device<"atmega2561", FamilyAVR6, ELFArchAVR6>; -def : Device<"atmega256rfr2", FamilyAVR6, ELFArchAVR6>; -def : Device<"atmega2564rfr2", FamilyAVR6, ELFArchAVR6>; -def : Device<"atxmega16a4", FamilyXMEGA, ELFArchXMEGA2>; -def : Device<"atxmega16a4u", FamilyXMEGAU, ELFArchXMEGA2>; -def : Device<"atxmega16c4", FamilyXMEGAU, ELFArchXMEGA2>; -def : Device<"atxmega16d4", FamilyXMEGA, ELFArchXMEGA2>; -def : Device<"atxmega32a4", FamilyXMEGA, ELFArchXMEGA2>; -def : Device<"atxmega32a4u", FamilyXMEGAU, ELFArchXMEGA2>; -def : Device<"atxmega32c4", FamilyXMEGAU, ELFArchXMEGA2>; -def : Device<"atxmega32d4", FamilyXMEGA, ELFArchXMEGA2>; -def : Device<"atxmega32e5", FamilyXMEGA, ELFArchXMEGA2>; -def : Device<"atxmega16e5", FamilyXMEGA, ELFArchXMEGA2>; -def : Device<"atxmega8e5", FamilyXMEGA, ELFArchXMEGA2>; -def : Device<"atxmega32x1", FamilyXMEGA, ELFArchXMEGA2>; -def : Device<"atxmega64a3", FamilyXMEGA, ELFArchXMEGA4>; -def : Device<"atxmega64a3u", FamilyXMEGAU, ELFArchXMEGA4>; -def : Device<"atxmega64a4u", FamilyXMEGAU, ELFArchXMEGA4>; -def : Device<"atxmega64b1", FamilyXMEGAU, ELFArchXMEGA4>; -def : Device<"atxmega64b3", FamilyXMEGAU, ELFArchXMEGA4>; -def : Device<"atxmega64c3", FamilyXMEGAU, ELFArchXMEGA4>; -def : Device<"atxmega64d3", FamilyXMEGA, ELFArchXMEGA4>; -def : Device<"atxmega64d4", FamilyXMEGA, ELFArchXMEGA4>; -def : Device<"atxmega64a1", FamilyXMEGA, ELFArchXMEGA5>; -def : Device<"atxmega64a1u", FamilyXMEGAU, ELFArchXMEGA5>; -def : Device<"atxmega128a3", FamilyXMEGA, ELFArchXMEGA6>; -def : Device<"atxmega128a3u", FamilyXMEGAU, ELFArchXMEGA6>; -def : Device<"atxmega128b1", FamilyXMEGAU, ELFArchXMEGA6>; -def : Device<"atxmega128b3", FamilyXMEGAU, ELFArchXMEGA6>; -def : Device<"atxmega128c3", FamilyXMEGAU, ELFArchXMEGA6>; -def : Device<"atxmega128d3", FamilyXMEGA, ELFArchXMEGA6>; -def : Device<"atxmega128d4", FamilyXMEGA, ELFArchXMEGA6>; -def : Device<"atxmega192a3", FamilyXMEGA, ELFArchXMEGA6>; -def : Device<"atxmega192a3u", FamilyXMEGAU, ELFArchXMEGA6>; -def : Device<"atxmega192c3", FamilyXMEGAU, ELFArchXMEGA6>; -def : Device<"atxmega192d3", FamilyXMEGA, ELFArchXMEGA6>; -def : Device<"atxmega256a3", FamilyXMEGA, ELFArchXMEGA6>; -def : Device<"atxmega256a3u", FamilyXMEGAU, ELFArchXMEGA6>; -def : Device<"atxmega256a3b", FamilyXMEGA, ELFArchXMEGA6>; -def : Device<"atxmega256a3bu", FamilyXMEGAU, ELFArchXMEGA6>; -def : Device<"atxmega256c3", FamilyXMEGAU, ELFArchXMEGA6>; -def : Device<"atxmega256d3", FamilyXMEGA, ELFArchXMEGA6>; -def : Device<"atxmega384c3", FamilyXMEGAU, ELFArchXMEGA6>; -def : Device<"atxmega384d3", FamilyXMEGA, ELFArchXMEGA6>; -def : Device<"atxmega128a1", FamilyXMEGA, ELFArchXMEGA7>; -def : Device<"atxmega128a1u", FamilyXMEGAU, ELFArchXMEGA7>; -def : Device<"atxmega128a4u", FamilyXMEGAU, ELFArchXMEGA7>; -def : Device<"attiny4", FamilyAVRTiny, ELFArchAVRTiny>; -def : Device<"attiny5", FamilyAVRTiny, ELFArchAVRTiny>; -def : Device<"attiny9", FamilyAVRTiny, ELFArchAVRTiny>; -def : Device<"attiny10", FamilyAVRTiny, ELFArchAVRTiny>; -def : Device<"attiny20", FamilyAVRTiny, ELFArchAVRTiny>; -def : Device<"attiny40", FamilyAVRTiny, ELFArchAVRTiny>; +include "AVRDevices.td" //===---------------------------------------------------------------------===// // Register File Description @@ -528,36 +45,37 @@ include "AVRCallingConv.td" // Assembly Printers //===---------------------------------------------------------------------===// -// def AVRAsmWriter : AsmWriter { -// string AsmWriterClassName = "InstPrinter"; -// bit isMCAsmWriter = 1; -// } +def AVRAsmWriter : AsmWriter { + string AsmWriterClassName = "InstPrinter"; + bit isMCAsmWriter = 1; +} //===---------------------------------------------------------------------===// // Assembly Parsers //===---------------------------------------------------------------------===// -// def AVRAsmParser : AsmParser { -// let ShouldEmitMatchRegisterName = 1; -// let ShouldEmitMatchRegisterAltName = 1; -// } +def AVRAsmParser : AsmParser { + let ShouldEmitMatchRegisterName = 1; + let ShouldEmitMatchRegisterAltName = 1; +} -// def AVRAsmParserVariant : AsmParserVariant { -// int Variant = 0; -// -// // Recognize hard coded registers. -// string RegisterPrefix = "$"; -// } +def AVRAsmParserVariant : AsmParserVariant { + int Variant = 0; + + // Recognize hard coded registers. + string RegisterPrefix = "$"; + string TokenizingCharacters = "+"; +} //===---------------------------------------------------------------------===// // Target Declaration //===---------------------------------------------------------------------===// def AVR : Target { - let InstructionSet = AVRInstrInfo; -// let AssemblyWriters = [AVRAsmWriter]; -// -// let AssemblyParsers = [AVRAsmParser]; -// let AssemblyParserVariants = [AVRAsmParserVariant]; + let InstructionSet = AVRInstrInfo; + let AssemblyWriters = [AVRAsmWriter]; + + let AssemblyParsers = [AVRAsmParser]; + let AssemblyParserVariants = [AVRAsmParserVariant]; } diff --git a/contrib/llvm/lib/Target/AVR/AVRAsmPrinter.cpp b/contrib/llvm/lib/Target/AVR/AVRAsmPrinter.cpp new file mode 100644 index 0000000..4afdd3a --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/AVRAsmPrinter.cpp @@ -0,0 +1,184 @@ +//===-- AVRAsmPrinter.cpp - AVR LLVM assembly writer ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains a printer that converts from our internal representation +// of machine-dependent LLVM code to GAS-format AVR assembly language. +// +//===----------------------------------------------------------------------===// + +#include "AVR.h" +#include "AVRMCInstLower.h" +#include "AVRSubtarget.h" +#include "InstPrinter/AVRInstPrinter.h" + +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/IR/Mangler.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetRegisterInfo.h" +#include "llvm/Target/TargetSubtargetInfo.h" + +#define DEBUG_TYPE "avr-asm-printer" + +namespace llvm { + +/// An AVR assembly code printer. +class AVRAsmPrinter : public AsmPrinter { +public: + AVRAsmPrinter(TargetMachine &TM, + std::unique_ptr<MCStreamer> Streamer) + : AsmPrinter(TM, std::move(Streamer)), MRI(*TM.getMCRegisterInfo()) { } + + StringRef getPassName() const override { return "AVR Assembly Printer"; } + + void printOperand(const MachineInstr *MI, unsigned OpNo, raw_ostream &O, + const char *Modifier = 0); + + bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNum, + unsigned AsmVariant, const char *ExtraCode, + raw_ostream &O) override; + + bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum, + unsigned AsmVariant, const char *ExtraCode, + raw_ostream &O) override; + + void EmitInstruction(const MachineInstr *MI) override; + +private: + const MCRegisterInfo &MRI; +}; + +void AVRAsmPrinter::printOperand(const MachineInstr *MI, unsigned OpNo, + raw_ostream &O, const char *Modifier) { + const MachineOperand &MO = MI->getOperand(OpNo); + + switch (MO.getType()) { + case MachineOperand::MO_Register: + O << AVRInstPrinter::getPrettyRegisterName(MO.getReg(), MRI); + break; + case MachineOperand::MO_Immediate: + O << MO.getImm(); + break; + case MachineOperand::MO_GlobalAddress: + O << getSymbol(MO.getGlobal()); + break; + case MachineOperand::MO_ExternalSymbol: + O << *GetExternalSymbolSymbol(MO.getSymbolName()); + break; + case MachineOperand::MO_MachineBasicBlock: + O << *MO.getMBB()->getSymbol(); + break; + default: + llvm_unreachable("Not implemented yet!"); + } +} + +bool AVRAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum, + unsigned AsmVariant, const char *ExtraCode, + raw_ostream &O) { + // Default asm printer can only deal with some extra codes, + // so try it first. + bool Error = AsmPrinter::PrintAsmOperand(MI, OpNum, AsmVariant, ExtraCode, O); + + if (Error && ExtraCode && ExtraCode[0]) { + if (ExtraCode[1] != 0) + return true; // Unknown modifier. + + if (ExtraCode[0] >= 'A' && ExtraCode[0] <= 'Z') { + const MachineOperand &RegOp = MI->getOperand(OpNum); + + assert(RegOp.isReg() && "Operand must be a register when you're" + "using 'A'..'Z' operand extracodes."); + unsigned Reg = RegOp.getReg(); + + unsigned ByteNumber = ExtraCode[0] - 'A'; + + unsigned OpFlags = MI->getOperand(OpNum - 1).getImm(); + unsigned NumOpRegs = InlineAsm::getNumOperandRegisters(OpFlags); + (void)NumOpRegs; + + const AVRSubtarget &STI = MF->getSubtarget<AVRSubtarget>(); + const TargetRegisterInfo &TRI = *STI.getRegisterInfo(); + + unsigned BytesPerReg = TRI.getMinimalPhysRegClass(Reg)->getSize(); + assert(BytesPerReg <= 2 && "Only 8 and 16 bit regs are supported."); + + unsigned RegIdx = ByteNumber / BytesPerReg; + assert(RegIdx < NumOpRegs && "Multibyte index out of range."); + + Reg = MI->getOperand(OpNum + RegIdx).getReg(); + + if (BytesPerReg == 2) { + Reg = TRI.getSubReg(Reg, ByteNumber % BytesPerReg ? AVR::sub_hi + : AVR::sub_lo); + } + + O << AVRInstPrinter::getPrettyRegisterName(Reg, MRI); + return false; + } + } + + printOperand(MI, OpNum, O); + + return false; +} + +bool AVRAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, + unsigned OpNum, unsigned AsmVariant, + const char *ExtraCode, + raw_ostream &O) { + if (ExtraCode && ExtraCode[0]) { + llvm_unreachable("This branch is not implemented yet"); + } + + const MachineOperand &MO = MI->getOperand(OpNum); + (void)MO; + assert(MO.isReg() && "Unexpected inline asm memory operand"); + + // TODO: We can look up the alternative name for the register if it's given. + if (MI->getOperand(OpNum).getReg() == AVR::R31R30) { + O << "Z"; + } else { + assert(MI->getOperand(OpNum).getReg() == AVR::R29R28 && + "Wrong register class for memory operand."); + O << "Y"; + } + + // If NumOpRegs == 2, then we assume it is product of a FrameIndex expansion + // and the second operand is an Imm. + unsigned OpFlags = MI->getOperand(OpNum - 1).getImm(); + unsigned NumOpRegs = InlineAsm::getNumOperandRegisters(OpFlags); + + if (NumOpRegs == 2) { + O << '+' << MI->getOperand(OpNum + 1).getImm(); + } + + return false; +} + +void AVRAsmPrinter::EmitInstruction(const MachineInstr *MI) { + AVRMCInstLower MCInstLowering(OutContext, *this); + + MCInst I; + MCInstLowering.lowerInstruction(*MI, I); + EmitToStreamer(*OutStreamer, I); +} + +} // end of namespace llvm + +extern "C" void LLVMInitializeAVRAsmPrinter() { + llvm::RegisterAsmPrinter<llvm::AVRAsmPrinter> X(llvm::getTheAVRTarget()); +} + diff --git a/contrib/llvm/lib/Target/AVR/AVRCallingConv.td b/contrib/llvm/lib/Target/AVR/AVRCallingConv.td index d8cb3fe..68dbce0 100644 --- a/contrib/llvm/lib/Target/AVR/AVRCallingConv.td +++ b/contrib/llvm/lib/Target/AVR/AVRCallingConv.td @@ -23,7 +23,7 @@ def RetCC_AVR : CallingConv ]>; // Special return value calling convention for runtime functions. -def RetCC_AVR_RT : CallingConv +def RetCC_AVR_BUILTIN : CallingConv <[ CCIfType<[i8], CCAssignToReg<[R24,R25]>>, CCIfType<[i16], CCAssignToReg<[R23R22, R25R24]>> @@ -43,15 +43,8 @@ def ArgCC_AVR_Vararg : CallingConv ]>; // Special argument calling convention for -// multiplication runtime functions. -def ArgCC_AVR_RT_MUL : CallingConv -<[ - CCIfType<[i16], CCAssignToReg<[R27R26,R19R18]>> -]>; - -// Special argument calling convention for // division runtime functions. -def ArgCC_AVR_RT_DIV : CallingConv +def ArgCC_AVR_BUILTIN_DIV : CallingConv <[ CCIfType<[i8], CCAssignToReg<[R24,R22]>>, CCIfType<[i16], CCAssignToReg<[R25R24, R23R22]>> diff --git a/contrib/llvm/lib/Target/AVR/AVRDevices.td b/contrib/llvm/lib/Target/AVR/AVRDevices.td new file mode 100644 index 0000000..9224af6 --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/AVRDevices.td @@ -0,0 +1,491 @@ +//===---------------------------------------------------------------------===// +// AVR Device Definitions +//===---------------------------------------------------------------------===// + +// :TODO: Implement the skip errata, see `gcc/config/avr/avr-arch.h` for details +// :TODO: We define all devices with SRAM to have all variants of LD/ST/LDD/STD. +// In reality, avr1 (no SRAM) has one variant each of `LD` and `ST`. +// avr2 (with SRAM) adds the rest of the variants. +// :TODO: s/AVRTiny/Tiny + + +// A feature set aggregates features, grouping them. We don't want to create a +// new member in AVRSubtarget (to store a value) for each set because we do not +// care if the set is supported, only the subfeatures inside the set. We fix +// this by simply setting the same dummy member for all feature sets, which is +// then ignored. +class FeatureSet<string name, string desc, list<SubtargetFeature> i> + : SubtargetFeature<name, "m_FeatureSetDummy", "true", desc, i>; + +// A family of microcontrollers, defining a set of supported features. +class Family<string name, list<SubtargetFeature> i> + : FeatureSet<name, !strconcat("The device is a part of the ", + name, " family"), i>; + +// The device has SRAM, and supports the bare minimum of +// SRAM-relevant instructions. +// +// These are: +// LD - all 9 variants +// ST - all 9 variants +// LDD - two variants for Y and Z +// STD - two variants for Y and Z +// `LDS Rd, K` +// `STS k, Rr` +// `PUSH`/`POP` +def FeatureSRAM : SubtargetFeature<"sram", "m_hasSRAM", "true", + "The device has random access memory">; + +// The device supports the `JMP k` and `CALL k` instructions. +def FeatureJMPCALL : SubtargetFeature<"jmpcall", "m_hasJMPCALL", "true", + "The device supports the `JMP` and " + "`CALL` instructions">; + + +// The device supports the indirect branches `IJMP` and `ICALL`. +def FeatureIJMPCALL : SubtargetFeature<"ijmpcall", "m_hasIJMPCALL", + "true", + "The device supports `IJMP`/`ICALL`" + "instructions">; + +// The device supports the extended indirect branches `EIJMP` and `EICALL`. +def FeatureEIJMPCALL : SubtargetFeature<"eijmpcall", "m_hasEIJMPCALL", + "true", "The device supports the " + "`EIJMP`/`EICALL` instructions">; + +// The device supports `ADDI Rd, K`, `SUBI Rd, K`. +def FeatureADDSUBIW : SubtargetFeature<"addsubiw", "m_hasADDSUBIW", + "true", "Enable 16-bit register-immediate " + "addition and subtraction instructions">; + +// The device has an 8-bit stack pointer (SP) register. +def FeatureSmallStack : SubtargetFeature<"smallstack", "m_hasSmallStack", + "true", "The device has an 8-bit " + "stack pointer">; + +// The device supports the 16-bit GPR pair MOVW instruction. +def FeatureMOVW : SubtargetFeature<"movw", "m_hasMOVW", "true", + "The device supports the 16-bit MOVW " + "instruction">; + +// The device supports the `LPM` instruction, with implied destination being r0. +def FeatureLPM : SubtargetFeature<"lpm", "m_hasLPM", "true", + "The device supports the `LPM` instruction">; + +// The device supports the `LPM Rd, Z[+] instruction. +def FeatureLPMX : SubtargetFeature<"lpmx", "m_hasLPMX", "true", + "The device supports the `LPM Rd, Z[+]` " + "instruction">; + +// The device supports the `ELPM` instruction. +def FeatureELPM : SubtargetFeature<"elpm", "m_hasELPM", "true", + "The device supports the ELPM instruction">; + +// The device supports the `ELPM Rd, Z[+]` instructions. +def FeatureELPMX : SubtargetFeature<"elpmx", "m_hasELPMX", "true", + "The device supports the `ELPM Rd, Z[+]` " + "instructions">; + +// The device supports the `SPM` instruction. +def FeatureSPM : SubtargetFeature<"spm", "m_hasSPM", "true", + "The device supports the `SPM` instruction">; + +// The device supports the `SPM Z+` instruction. +def FeatureSPMX : SubtargetFeature<"spmx", "m_hasSPMX", "true", + "The device supports the `SPM Z+` " + "instruction">; + +// The device supports the `DES k` instruction. +def FeatureDES : SubtargetFeature<"des", "m_hasDES", "true", + "The device supports the `DES k` encryption " + "instruction">; + +// The device supports the Read-Write-Modify instructions +// XCH, LAS, LAC, and LAT. +def FeatureRMW : SubtargetFeature<"rmw", "m_supportsRMW", "true", + "The device supports the read-write-modify " + "instructions: XCH, LAS, LAC, LAT">; + +// The device supports the `[F]MUL[S][U]` family of instructions. +def FeatureMultiplication : SubtargetFeature<"mul", "m_supportsMultiplication", + "true", "The device supports the " + "multiplication instructions">; + +// The device supports the `BREAK` instruction. +def FeatureBREAK : SubtargetFeature<"break", "m_hasBREAK", "true", + "The device supports the `BREAK` debugging " + "instruction">; + +// The device has instruction encodings specific to the Tiny core. +def FeatureTinyEncoding : SubtargetFeature<"tinyencoding", + "m_hasTinyEncoding", "true", + "The device has Tiny core specific " + "instruction encodings">; + +class ELFArch<string name> : SubtargetFeature<"", "ELFArch", + !strconcat("ELF::",name), "">; + +// ELF e_flags architecture values +def ELFArchAVR1 : ELFArch<"EF_AVR_ARCH_AVR1">; +def ELFArchAVR2 : ELFArch<"EF_AVR_ARCH_AVR2">; +def ELFArchAVR25 : ELFArch<"EF_AVR_ARCH_AVR25">; +def ELFArchAVR3 : ELFArch<"EF_AVR_ARCH_AVR3">; +def ELFArchAVR31 : ELFArch<"EF_AVR_ARCH_AVR31">; +def ELFArchAVR35 : ELFArch<"EF_AVR_ARCH_AVR35">; +def ELFArchAVR4 : ELFArch<"EF_AVR_ARCH_AVR4">; +def ELFArchAVR5 : ELFArch<"EF_AVR_ARCH_AVR5">; +def ELFArchAVR51 : ELFArch<"EF_AVR_ARCH_AVR51">; +def ELFArchAVR6 : ELFArch<"EF_AVR_ARCH_AVR6">; +def ELFArchAVRTiny : ELFArch<"EF_AVR_ARCH_AVRTINY">; +def ELFArchXMEGA1 : ELFArch<"EF_AVR_ARCH_XMEGA1">; +def ELFArchXMEGA2 : ELFArch<"EF_AVR_ARCH_XMEGA2">; +def ELFArchXMEGA3 : ELFArch<"EF_AVR_ARCH_XMEGA3">; +def ELFArchXMEGA4 : ELFArch<"EF_AVR_ARCH_XMEGA4">; +def ELFArchXMEGA5 : ELFArch<"EF_AVR_ARCH_XMEGA5">; +def ELFArchXMEGA6 : ELFArch<"EF_AVR_ARCH_XMEGA6">; +def ELFArchXMEGA7 : ELFArch<"EF_AVR_ARCH_XMEGA7">; + +//===---------------------------------------------------------------------===// +// AVR Families +//===---------------------------------------------------------------------===// + +// The device has at least the bare minimum that **every** single AVR +// device should have. +def FamilyAVR0 : Family<"avr0", []>; + +def FamilyAVR1 : Family<"avr1", [FamilyAVR0, FeatureLPM]>; + +def FamilyAVR2 : Family<"avr2", + [FamilyAVR1, FeatureIJMPCALL, FeatureADDSUBIW, + FeatureSRAM]>; + +def FamilyAVR25 : Family<"avr25", + [FamilyAVR2, FeatureMOVW, FeatureLPMX, + FeatureSPM, FeatureBREAK]>; + +def FamilyAVR3 : Family<"avr3", + [FamilyAVR2, FeatureJMPCALL]>; + +def FamilyAVR31 : Family<"avr31", + [FamilyAVR3, FeatureELPM]>; + +def FamilyAVR35 : Family<"avr35", + [FamilyAVR3, FeatureMOVW, FeatureLPMX, + FeatureSPM, FeatureBREAK]>; + +def FamilyAVR4 : Family<"avr4", + [FamilyAVR2, FeatureMultiplication, + FeatureMOVW, FeatureLPMX, FeatureSPM, + FeatureBREAK]>; + +def FamilyAVR5 : Family<"avr5", + [FamilyAVR3, FeatureMultiplication, + FeatureMOVW, FeatureLPMX, FeatureSPM, + FeatureBREAK]>; + +def FamilyAVR51 : Family<"avr51", + [FamilyAVR5, FeatureELPM, FeatureELPMX]>; + +def FamilyAVR6 : Family<"avr6", + [FamilyAVR51]>; + +def FamilyAVRTiny : Family<"avrtiny", + [FamilyAVR0, FeatureBREAK, FeatureSRAM, + FeatureTinyEncoding]>; + +def FamilyXMEGA : Family<"xmega", + [FamilyAVR51, FeatureEIJMPCALL, FeatureSPMX, + FeatureDES]>; + +def FamilyXMEGAU : Family<"xmegau", + [FamilyXMEGA, FeatureRMW]>; + +def FeatureSetSpecial : FeatureSet<"special", + "Enable use of the entire instruction " + "set - used for debugging", + [FeatureSRAM, FeatureJMPCALL, + FeatureIJMPCALL, FeatureEIJMPCALL, + FeatureADDSUBIW, FeatureMOVW, + FeatureLPM, FeatureLPMX, FeatureELPM, + FeatureELPMX, FeatureSPM, FeatureSPMX, + FeatureDES, FeatureRMW, + FeatureMultiplication, FeatureBREAK]>; + +//===---------------------------------------------------------------------===// +// AVR microcontrollers supported. +//===---------------------------------------------------------------------===// + +class Device<string Name, Family Fam, ELFArch Arch, + list<SubtargetFeature> ExtraFeatures = []> + : Processor<Name, NoItineraries, !listconcat([Fam,Arch],ExtraFeatures)>; + +// Generic MCUs +// Note that several versions of GCC has strange ELF architecture +// settings for backwards compatibility - see `gas/config/tc-avr.c` +// in AVR binutils. We do not replicate this. +def : Device<"avr1", FamilyAVR1, ELFArchAVR1>; +def : Device<"avr2", FamilyAVR2, ELFArchAVR2>; +def : Device<"avr25", FamilyAVR25, ELFArchAVR25>; +def : Device<"avr3", FamilyAVR3, ELFArchAVR3>; +def : Device<"avr31", FamilyAVR31, ELFArchAVR31>; +def : Device<"avr35", FamilyAVR35, ELFArchAVR35>; +def : Device<"avr4", FamilyAVR4, ELFArchAVR4>; +def : Device<"avr5", FamilyAVR5, ELFArchAVR5>; +def : Device<"avr51", FamilyAVR51, ELFArchAVR51>; +def : Device<"avr6", FamilyAVR6, ELFArchAVR6>; +def : Device<"avrxmega1", FamilyXMEGA, ELFArchXMEGA1>; +def : Device<"avrxmega2", FamilyXMEGA, ELFArchXMEGA2>; +def : Device<"avrxmega3", FamilyXMEGA, ELFArchXMEGA3>; +def : Device<"avrxmega4", FamilyXMEGA, ELFArchXMEGA4>; +def : Device<"avrxmega5", FamilyXMEGA, ELFArchXMEGA5>; +def : Device<"avrxmega6", FamilyXMEGA, ELFArchXMEGA6>; +def : Device<"avrxmega7", FamilyXMEGA, ELFArchXMEGA7>; +def : Device<"avrtiny", FamilyAVRTiny, ELFArchAVRTiny>; + +// Specific MCUs +def : Device<"at90s1200", FamilyAVR0, ELFArchAVR1>; +def : Device<"attiny11", FamilyAVR1, ELFArchAVR1>; +def : Device<"attiny12", FamilyAVR1, ELFArchAVR1>; +def : Device<"attiny15", FamilyAVR1, ELFArchAVR1>; +def : Device<"attiny28", FamilyAVR1, ELFArchAVR1>; +def : Device<"at90s2313", FamilyAVR2, ELFArchAVR2>; +def : Device<"at90s2323", FamilyAVR2, ELFArchAVR2>; +def : Device<"at90s2333", FamilyAVR2, ELFArchAVR2>; +def : Device<"at90s2343", FamilyAVR2, ELFArchAVR2>; +def : Device<"attiny22", FamilyAVR2, ELFArchAVR2>; +def : Device<"attiny26", FamilyAVR2, ELFArchAVR2, [FeatureLPMX]>; +def : Device<"at86rf401", FamilyAVR2, ELFArchAVR25, + [FeatureMOVW, FeatureLPMX]>; +def : Device<"at90s4414", FamilyAVR2, ELFArchAVR2>; +def : Device<"at90s4433", FamilyAVR2, ELFArchAVR2>; +def : Device<"at90s4434", FamilyAVR2, ELFArchAVR2>; +def : Device<"at90s8515", FamilyAVR2, ELFArchAVR2>; +def : Device<"at90c8534", FamilyAVR2, ELFArchAVR2>; +def : Device<"at90s8535", FamilyAVR2, ELFArchAVR2>; +def : Device<"ata5272", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny13", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny13a", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny2313", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny2313a", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny24", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny24a", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny4313", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny44", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny44a", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny84", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny84a", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny25", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny45", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny85", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny261", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny261a", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny461", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny461a", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny861", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny861a", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny87", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny43u", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny48", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny88", FamilyAVR25, ELFArchAVR25>; +def : Device<"attiny828", FamilyAVR25, ELFArchAVR25>; +def : Device<"at43usb355", FamilyAVR3, ELFArchAVR3>; +def : Device<"at76c711", FamilyAVR3, ELFArchAVR3>; +def : Device<"atmega103", FamilyAVR31, ELFArchAVR31>; +def : Device<"at43usb320", FamilyAVR31, ELFArchAVR31>; +def : Device<"attiny167", FamilyAVR35, ELFArchAVR35>; +def : Device<"at90usb82", FamilyAVR35, ELFArchAVR35>; +def : Device<"at90usb162", FamilyAVR35, ELFArchAVR35>; +def : Device<"ata5505", FamilyAVR35, ELFArchAVR35>; +def : Device<"atmega8u2", FamilyAVR35, ELFArchAVR35>; +def : Device<"atmega16u2", FamilyAVR35, ELFArchAVR35>; +def : Device<"atmega32u2", FamilyAVR35, ELFArchAVR35>; +def : Device<"attiny1634", FamilyAVR35, ELFArchAVR35>; +def : Device<"atmega8", FamilyAVR4, ELFArchAVR4>; // FIXME: family may be wrong +def : Device<"ata6289", FamilyAVR4, ELFArchAVR4>; +def : Device<"atmega8a", FamilyAVR4, ELFArchAVR4>; +def : Device<"ata6285", FamilyAVR4, ELFArchAVR4>; +def : Device<"ata6286", FamilyAVR4, ELFArchAVR4>; +def : Device<"atmega48", FamilyAVR4, ELFArchAVR4>; +def : Device<"atmega48a", FamilyAVR4, ELFArchAVR4>; +def : Device<"atmega48pa", FamilyAVR4, ELFArchAVR4>; +def : Device<"atmega48p", FamilyAVR4, ELFArchAVR4>; +def : Device<"atmega88", FamilyAVR4, ELFArchAVR4>; +def : Device<"atmega88a", FamilyAVR4, ELFArchAVR4>; +def : Device<"atmega88p", FamilyAVR4, ELFArchAVR4>; +def : Device<"atmega88pa", FamilyAVR4, ELFArchAVR4>; +def : Device<"atmega8515", FamilyAVR2, ELFArchAVR4, + [FeatureMultiplication, FeatureMOVW, FeatureLPMX, FeatureSPM]>; +def : Device<"atmega8535", FamilyAVR2, ELFArchAVR4, + [FeatureMultiplication, FeatureMOVW, FeatureLPMX, FeatureSPM]>; +def : Device<"atmega8hva", FamilyAVR4, ELFArchAVR4>; +def : Device<"at90pwm1", FamilyAVR4, ELFArchAVR4>; +def : Device<"at90pwm2", FamilyAVR4, ELFArchAVR4>; +def : Device<"at90pwm2b", FamilyAVR4, ELFArchAVR4>; +def : Device<"at90pwm3", FamilyAVR4, ELFArchAVR4>; +def : Device<"at90pwm3b", FamilyAVR4, ELFArchAVR4>; +def : Device<"at90pwm81", FamilyAVR4, ELFArchAVR4>; +def : Device<"ata5790", FamilyAVR5, ELFArchAVR5>; +def : Device<"ata5795", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega16", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega16a", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega161", FamilyAVR3, ELFArchAVR5, + [FeatureMultiplication, FeatureMOVW, FeatureLPMX, FeatureSPM]>; +def : Device<"atmega162", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega163", FamilyAVR3, ELFArchAVR5, + [FeatureMultiplication, FeatureMOVW, FeatureLPMX, FeatureSPM]>; +def : Device<"atmega164a", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega164p", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega164pa", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega165", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega165a", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega165p", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega165pa", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega168", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega168a", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega168p", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega168pa", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega169", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega169a", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega169p", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega169pa", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega32", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega32a", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega323", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega324a", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega324p", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega324pa", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega325", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega325a", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega325p", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega325pa", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega3250", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega3250a", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega3250p", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega3250pa", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega328", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega328p", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega329", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega329a", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega329p", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega329pa", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega3290", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega3290a", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega3290p", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega3290pa", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega406", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega64", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega64a", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega640", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega644", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega644a", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega644p", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega644pa", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega645", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega645a", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega645p", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega649", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega649a", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega649p", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega6450", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega6450a", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega6450p", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega6490", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega6490a", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega6490p", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega64rfr2", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega644rfr2", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega16hva", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega16hva2", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega16hvb", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega16hvbrevb", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega32hvb", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega32hvbrevb", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega64hve", FamilyAVR5, ELFArchAVR5>; +def : Device<"at90can32", FamilyAVR5, ELFArchAVR5>; +def : Device<"at90can64", FamilyAVR5, ELFArchAVR5>; +def : Device<"at90pwm161", FamilyAVR5, ELFArchAVR5>; +def : Device<"at90pwm216", FamilyAVR5, ELFArchAVR5>; +def : Device<"at90pwm316", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega32c1", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega64c1", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega16m1", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega32m1", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega64m1", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega16u4", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega32u4", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega32u6", FamilyAVR5, ELFArchAVR5>; +def : Device<"at90usb646", FamilyAVR5, ELFArchAVR5>; +def : Device<"at90usb647", FamilyAVR5, ELFArchAVR5>; +def : Device<"at90scr100", FamilyAVR5, ELFArchAVR5>; +def : Device<"at94k", FamilyAVR3, ELFArchAVR5, + [FeatureMultiplication, FeatureMOVW, FeatureLPMX]>; +def : Device<"m3000", FamilyAVR5, ELFArchAVR5>; +def : Device<"atmega128", FamilyAVR51, ELFArchAVR51>; +def : Device<"atmega128a", FamilyAVR51, ELFArchAVR51>; +def : Device<"atmega1280", FamilyAVR51, ELFArchAVR51>; +def : Device<"atmega1281", FamilyAVR51, ELFArchAVR51>; +def : Device<"atmega1284", FamilyAVR51, ELFArchAVR51>; +def : Device<"atmega1284p", FamilyAVR51, ELFArchAVR51>; +def : Device<"atmega128rfa1", FamilyAVR51, ELFArchAVR51>; +def : Device<"atmega128rfr2", FamilyAVR51, ELFArchAVR51>; +def : Device<"atmega1284rfr2", FamilyAVR51, ELFArchAVR51>; +def : Device<"at90can128", FamilyAVR51, ELFArchAVR51>; +def : Device<"at90usb1286", FamilyAVR51, ELFArchAVR51>; +def : Device<"at90usb1287", FamilyAVR51, ELFArchAVR51>; +def : Device<"atmega2560", FamilyAVR6, ELFArchAVR6>; +def : Device<"atmega2561", FamilyAVR6, ELFArchAVR6>; +def : Device<"atmega256rfr2", FamilyAVR6, ELFArchAVR6>; +def : Device<"atmega2564rfr2", FamilyAVR6, ELFArchAVR6>; +def : Device<"atxmega16a4", FamilyXMEGA, ELFArchXMEGA2>; +def : Device<"atxmega16a4u", FamilyXMEGAU, ELFArchXMEGA2>; +def : Device<"atxmega16c4", FamilyXMEGAU, ELFArchXMEGA2>; +def : Device<"atxmega16d4", FamilyXMEGA, ELFArchXMEGA2>; +def : Device<"atxmega32a4", FamilyXMEGA, ELFArchXMEGA2>; +def : Device<"atxmega32a4u", FamilyXMEGAU, ELFArchXMEGA2>; +def : Device<"atxmega32c4", FamilyXMEGAU, ELFArchXMEGA2>; +def : Device<"atxmega32d4", FamilyXMEGA, ELFArchXMEGA2>; +def : Device<"atxmega32e5", FamilyXMEGA, ELFArchXMEGA2>; +def : Device<"atxmega16e5", FamilyXMEGA, ELFArchXMEGA2>; +def : Device<"atxmega8e5", FamilyXMEGA, ELFArchXMEGA2>; +def : Device<"atxmega32x1", FamilyXMEGA, ELFArchXMEGA2>; +def : Device<"atxmega64a3", FamilyXMEGA, ELFArchXMEGA4>; +def : Device<"atxmega64a3u", FamilyXMEGAU, ELFArchXMEGA4>; +def : Device<"atxmega64a4u", FamilyXMEGAU, ELFArchXMEGA4>; +def : Device<"atxmega64b1", FamilyXMEGAU, ELFArchXMEGA4>; +def : Device<"atxmega64b3", FamilyXMEGAU, ELFArchXMEGA4>; +def : Device<"atxmega64c3", FamilyXMEGAU, ELFArchXMEGA4>; +def : Device<"atxmega64d3", FamilyXMEGA, ELFArchXMEGA4>; +def : Device<"atxmega64d4", FamilyXMEGA, ELFArchXMEGA4>; +def : Device<"atxmega64a1", FamilyXMEGA, ELFArchXMEGA5>; +def : Device<"atxmega64a1u", FamilyXMEGAU, ELFArchXMEGA5>; +def : Device<"atxmega128a3", FamilyXMEGA, ELFArchXMEGA6>; +def : Device<"atxmega128a3u", FamilyXMEGAU, ELFArchXMEGA6>; +def : Device<"atxmega128b1", FamilyXMEGAU, ELFArchXMEGA6>; +def : Device<"atxmega128b3", FamilyXMEGAU, ELFArchXMEGA6>; +def : Device<"atxmega128c3", FamilyXMEGAU, ELFArchXMEGA6>; +def : Device<"atxmega128d3", FamilyXMEGA, ELFArchXMEGA6>; +def : Device<"atxmega128d4", FamilyXMEGA, ELFArchXMEGA6>; +def : Device<"atxmega192a3", FamilyXMEGA, ELFArchXMEGA6>; +def : Device<"atxmega192a3u", FamilyXMEGAU, ELFArchXMEGA6>; +def : Device<"atxmega192c3", FamilyXMEGAU, ELFArchXMEGA6>; +def : Device<"atxmega192d3", FamilyXMEGA, ELFArchXMEGA6>; +def : Device<"atxmega256a3", FamilyXMEGA, ELFArchXMEGA6>; +def : Device<"atxmega256a3u", FamilyXMEGAU, ELFArchXMEGA6>; +def : Device<"atxmega256a3b", FamilyXMEGA, ELFArchXMEGA6>; +def : Device<"atxmega256a3bu", FamilyXMEGAU, ELFArchXMEGA6>; +def : Device<"atxmega256c3", FamilyXMEGAU, ELFArchXMEGA6>; +def : Device<"atxmega256d3", FamilyXMEGA, ELFArchXMEGA6>; +def : Device<"atxmega384c3", FamilyXMEGAU, ELFArchXMEGA6>; +def : Device<"atxmega384d3", FamilyXMEGA, ELFArchXMEGA6>; +def : Device<"atxmega128a1", FamilyXMEGA, ELFArchXMEGA7>; +def : Device<"atxmega128a1u", FamilyXMEGAU, ELFArchXMEGA7>; +def : Device<"atxmega128a4u", FamilyXMEGAU, ELFArchXMEGA7>; +def : Device<"attiny4", FamilyAVRTiny, ELFArchAVRTiny>; +def : Device<"attiny5", FamilyAVRTiny, ELFArchAVRTiny>; +def : Device<"attiny9", FamilyAVRTiny, ELFArchAVRTiny>; +def : Device<"attiny10", FamilyAVRTiny, ELFArchAVRTiny>; +def : Device<"attiny20", FamilyAVRTiny, ELFArchAVRTiny>; +def : Device<"attiny40", FamilyAVRTiny, ELFArchAVRTiny>; +def : Device<"attiny102", FamilyAVRTiny, ELFArchAVRTiny>; +def : Device<"attiny104", FamilyAVRTiny, ELFArchAVRTiny>; + diff --git a/contrib/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/contrib/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp new file mode 100644 index 0000000..1b2f2ce --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -0,0 +1,1515 @@ +//===-- AVRExpandPseudoInsts.cpp - Expand pseudo instructions -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains a pass that expands pseudo instructions into target +// instructions. This pass should be run after register allocation but before +// the post-regalloc scheduling pass. +// +//===----------------------------------------------------------------------===// + +#include "AVR.h" +#include "AVRInstrInfo.h" +#include "AVRTargetMachine.h" +#include "MCTargetDesc/AVRMCTargetDesc.h" + +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/RegisterScavenging.h" +#include "llvm/Target/TargetRegisterInfo.h" + +using namespace llvm; + +#define AVR_EXPAND_PSEUDO_NAME "AVR pseudo instruction expansion pass" + +namespace { + +/// Expands "placeholder" instructions marked as pseudo into +/// actual AVR instructions. +class AVRExpandPseudo : public MachineFunctionPass { +public: + static char ID; + + AVRExpandPseudo() : MachineFunctionPass(ID) { + initializeAVRExpandPseudoPass(*PassRegistry::getPassRegistry()); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + + StringRef getPassName() const override { return AVR_EXPAND_PSEUDO_NAME; } + +private: + typedef MachineBasicBlock Block; + typedef Block::iterator BlockIt; + + const AVRRegisterInfo *TRI; + const TargetInstrInfo *TII; + + /// The register to be used for temporary storage. + const unsigned SCRATCH_REGISTER = AVR::R0; + /// The IO address of the status register. + const unsigned SREG_ADDR = 0x3f; + + bool expandMBB(Block &MBB); + bool expandMI(Block &MBB, BlockIt MBBI); + template <unsigned OP> bool expand(Block &MBB, BlockIt MBBI); + + MachineInstrBuilder buildMI(Block &MBB, BlockIt MBBI, unsigned Opcode) { + return BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(Opcode)); + } + + MachineInstrBuilder buildMI(Block &MBB, BlockIt MBBI, unsigned Opcode, + unsigned DstReg) { + return BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(Opcode), DstReg); + } + + MachineRegisterInfo &getRegInfo(Block &MBB) { return MBB.getParent()->getRegInfo(); } + + bool expandArith(unsigned OpLo, unsigned OpHi, Block &MBB, BlockIt MBBI); + bool expandLogic(unsigned Op, Block &MBB, BlockIt MBBI); + bool expandLogicImm(unsigned Op, Block &MBB, BlockIt MBBI); + bool isLogicImmOpRedundant(unsigned Op, unsigned ImmVal) const; + + template<typename Func> + bool expandAtomic(Block &MBB, BlockIt MBBI, Func f); + + template<typename Func> + bool expandAtomicBinaryOp(unsigned Opcode, Block &MBB, BlockIt MBBI, Func f); + + bool expandAtomicBinaryOp(unsigned Opcode, Block &MBB, BlockIt MBBI); + + bool expandAtomicArithmeticOp(unsigned MemOpcode, + unsigned ArithOpcode, + Block &MBB, + BlockIt MBBI); +}; + +char AVRExpandPseudo::ID = 0; + +bool AVRExpandPseudo::expandMBB(MachineBasicBlock &MBB) { + bool Modified = false; + + BlockIt MBBI = MBB.begin(), E = MBB.end(); + while (MBBI != E) { + BlockIt NMBBI = std::next(MBBI); + Modified |= expandMI(MBB, MBBI); + MBBI = NMBBI; + } + + return Modified; +} + +bool AVRExpandPseudo::runOnMachineFunction(MachineFunction &MF) { + bool Modified = false; + + const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>(); + TRI = STI.getRegisterInfo(); + TII = STI.getInstrInfo(); + + // We need to track liveness in order to use register scavenging. + MF.getProperties().set(MachineFunctionProperties::Property::TracksLiveness); + + for (Block &MBB : MF) { + bool ContinueExpanding = true; + unsigned ExpandCount = 0; + + // Continue expanding the block until all pseudos are expanded. + do { + assert(ExpandCount < 10 && "pseudo expand limit reached"); + + bool BlockModified = expandMBB(MBB); + Modified |= BlockModified; + ExpandCount++; + + ContinueExpanding = BlockModified; + } while (ContinueExpanding); + } + + return Modified; +} + +bool AVRExpandPseudo:: +expandArith(unsigned OpLo, unsigned OpHi, Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned SrcLoReg, SrcHiReg, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(2).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool SrcIsKill = MI.getOperand(2).isKill(); + bool ImpIsDead = MI.getOperand(3).isDead(); + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + if (ImpIsDead) + MIBHI->getOperand(3).setIsDead(); + + // SREG is always implicitly killed + MIBHI->getOperand(4).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +bool AVRExpandPseudo:: +expandLogic(unsigned Op, Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned SrcLoReg, SrcHiReg, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(2).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool SrcIsKill = MI.getOperand(2).isKill(); + bool ImpIsDead = MI.getOperand(3).isDead(); + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + auto MIBLO = buildMI(MBB, MBBI, Op) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + // SREG is always implicitly dead + MIBLO->getOperand(3).setIsDead(); + + auto MIBHI = buildMI(MBB, MBBI, Op) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + if (ImpIsDead) + MIBHI->getOperand(3).setIsDead(); + + MI.eraseFromParent(); + return true; +} + +bool AVRExpandPseudo:: + isLogicImmOpRedundant(unsigned Op, unsigned ImmVal) const { + + // ANDI Rd, 0xff is redundant. + if (Op == AVR::ANDIRdK && ImmVal == 0xff) + return true; + + // ORI Rd, 0x0 is redundant. + if (Op == AVR::ORIRdK && ImmVal == 0x0) + return true; + + return false; +} + +bool AVRExpandPseudo:: +expandLogicImm(unsigned Op, Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(3).isDead(); + unsigned Imm = MI.getOperand(2).getImm(); + unsigned Lo8 = Imm & 0xff; + unsigned Hi8 = (Imm >> 8) & 0xff; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + if (!isLogicImmOpRedundant(Op, Lo8)) { + auto MIBLO = buildMI(MBB, MBBI, Op) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(SrcIsKill)) + .addImm(Lo8); + + // SREG is always implicitly dead + MIBLO->getOperand(3).setIsDead(); + } + + if (!isLogicImmOpRedundant(Op, Hi8)) { + auto MIBHI = buildMI(MBB, MBBI, Op) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(SrcIsKill)) + .addImm(Hi8); + + if (ImpIsDead) + MIBHI->getOperand(3).setIsDead(); + } + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::ADDWRdRr>(Block &MBB, BlockIt MBBI) { + return expandArith(AVR::ADDRdRr, AVR::ADCRdRr, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::ADCWRdRr>(Block &MBB, BlockIt MBBI) { + return expandArith(AVR::ADCRdRr, AVR::ADCRdRr, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::SUBWRdRr>(Block &MBB, BlockIt MBBI) { + return expandArith(AVR::SUBRdRr, AVR::SBCRdRr, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::SUBIWRdK>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(3).isDead(); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + auto MIBLO = buildMI(MBB, MBBI, AVR::SUBIRdK) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(SrcIsKill)); + + auto MIBHI = buildMI(MBB, MBBI, AVR::SBCIRdK) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(SrcIsKill)); + + switch (MI.getOperand(2).getType()) { + case MachineOperand::MO_GlobalAddress: { + const GlobalValue *GV = MI.getOperand(2).getGlobal(); + int64_t Offs = MI.getOperand(2).getOffset(); + unsigned TF = MI.getOperand(2).getTargetFlags(); + MIBLO.addGlobalAddress(GV, Offs, TF | AVRII::MO_NEG | AVRII::MO_LO); + MIBHI.addGlobalAddress(GV, Offs, TF | AVRII::MO_NEG | AVRII::MO_HI); + break; + } + case MachineOperand::MO_Immediate: { + unsigned Imm = MI.getOperand(2).getImm(); + MIBLO.addImm(Imm & 0xff); + MIBHI.addImm((Imm >> 8) & 0xff); + break; + } + default: + llvm_unreachable("Unknown operand type!"); + } + + if (ImpIsDead) + MIBHI->getOperand(3).setIsDead(); + + // SREG is always implicitly killed + MIBHI->getOperand(4).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::SBCWRdRr>(Block &MBB, BlockIt MBBI) { + return expandArith(AVR::SBCRdRr, AVR::SBCRdRr, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::SBCIWRdK>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(3).isDead(); + unsigned Imm = MI.getOperand(2).getImm(); + unsigned Lo8 = Imm & 0xff; + unsigned Hi8 = (Imm >> 8) & 0xff; + OpLo = AVR::SBCIRdK; + OpHi = AVR::SBCIRdK; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(SrcIsKill)) + .addImm(Lo8); + + // SREG is always implicitly killed + MIBLO->getOperand(4).setIsKill(); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(SrcIsKill)) + .addImm(Hi8); + + if (ImpIsDead) + MIBHI->getOperand(3).setIsDead(); + + // SREG is always implicitly killed + MIBHI->getOperand(4).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::ANDWRdRr>(Block &MBB, BlockIt MBBI) { + return expandLogic(AVR::ANDRdRr, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::ANDIWRdK>(Block &MBB, BlockIt MBBI) { + return expandLogicImm(AVR::ANDIRdK, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::ORWRdRr>(Block &MBB, BlockIt MBBI) { + return expandLogic(AVR::ORRdRr, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::ORIWRdK>(Block &MBB, BlockIt MBBI) { + return expandLogicImm(AVR::ORIRdK, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::EORWRdRr>(Block &MBB, BlockIt MBBI) { + return expandLogic(AVR::EORRdRr, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::COMWRd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + OpLo = AVR::COMRd; + OpHi = AVR::COMRd; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)); + + // SREG is always implicitly dead + MIBLO->getOperand(2).setIsDead(); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + + if (ImpIsDead) + MIBHI->getOperand(2).setIsDead(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::CPWRdRr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool DstIsKill = MI.getOperand(0).isKill(); + bool SrcIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + OpLo = AVR::CPRdRr; + OpHi = AVR::CPCRdRr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + // Low part + buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, getKillRegState(DstIsKill)) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + if (ImpIsDead) + MIBHI->getOperand(2).setIsDead(); + + // SREG is always implicitly killed + MIBHI->getOperand(3).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::CPCWRdRr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool DstIsKill = MI.getOperand(0).isKill(); + bool SrcIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + OpLo = AVR::CPCRdRr; + OpHi = AVR::CPCRdRr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, getKillRegState(DstIsKill)) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + // SREG is always implicitly killed + MIBLO->getOperand(3).setIsKill(); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + if (ImpIsDead) + MIBHI->getOperand(2).setIsDead(); + + // SREG is always implicitly killed + MIBHI->getOperand(3).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::LDIWRdK>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + OpLo = AVR::LDIRdK; + OpHi = AVR::LDIRdK; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)); + + switch (MI.getOperand(1).getType()) { + case MachineOperand::MO_GlobalAddress: { + const GlobalValue *GV = MI.getOperand(1).getGlobal(); + int64_t Offs = MI.getOperand(1).getOffset(); + unsigned TF = MI.getOperand(1).getTargetFlags(); + + MIBLO.addGlobalAddress(GV, Offs, TF | AVRII::MO_LO); + MIBHI.addGlobalAddress(GV, Offs, TF | AVRII::MO_HI); + break; + } + case MachineOperand::MO_BlockAddress: { + const BlockAddress *BA = MI.getOperand(1).getBlockAddress(); + unsigned TF = MI.getOperand(1).getTargetFlags(); + + MIBLO.addOperand(MachineOperand::CreateBA(BA, TF | AVRII::MO_LO)); + MIBHI.addOperand(MachineOperand::CreateBA(BA, TF | AVRII::MO_HI)); + break; + } + case MachineOperand::MO_Immediate: { + unsigned Imm = MI.getOperand(1).getImm(); + + MIBLO.addImm(Imm & 0xff); + MIBHI.addImm((Imm >> 8) & 0xff); + break; + } + default: + llvm_unreachable("Unknown operand type!"); + } + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::LDSWRdK>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + OpLo = AVR::LDSRdK; + OpHi = AVR::LDSRdK; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)); + + switch (MI.getOperand(1).getType()) { + case MachineOperand::MO_GlobalAddress: { + const GlobalValue *GV = MI.getOperand(1).getGlobal(); + int64_t Offs = MI.getOperand(1).getOffset(); + unsigned TF = MI.getOperand(1).getTargetFlags(); + + MIBLO.addGlobalAddress(GV, Offs, TF); + MIBHI.addGlobalAddress(GV, Offs + 1, TF); + break; + } + case MachineOperand::MO_Immediate: { + unsigned Imm = MI.getOperand(1).getImm(); + + MIBLO.addImm(Imm); + MIBHI.addImm(Imm + 1); + break; + } + default: + llvm_unreachable("Unknown operand type!"); + } + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::LDWRdPtr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(1).isKill(); + OpLo = AVR::LDRdPtr; + OpHi = AVR::LDDRdPtrQ; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same"); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg, getKillRegState(SrcIsKill)) + .addImm(1); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::LDWRdPtrPi>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsDead = MI.getOperand(1).isKill(); + OpLo = AVR::LDRdPtrPi; + OpHi = AVR::LDRdPtrPi; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same"); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg, RegState::Define) + .addReg(SrcReg, RegState::Kill); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg, RegState::Define | getDeadRegState(SrcIsDead)) + .addReg(SrcReg, RegState::Kill); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::LDWRdPtrPd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsDead = MI.getOperand(1).isKill(); + OpLo = AVR::LDRdPtrPd; + OpHi = AVR::LDRdPtrPd; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same"); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg, RegState::Define) + .addReg(SrcReg, RegState::Kill); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg, RegState::Define | getDeadRegState(SrcIsDead)) + .addReg(SrcReg, RegState::Kill); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::LDDWRdPtrQ>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + unsigned Imm = MI.getOperand(2).getImm(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(1).isKill(); + OpLo = AVR::LDDRdPtrQ; + OpHi = AVR::LDDRdPtrQ; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + assert(Imm <= 63 && "Offset is out of range"); + + MachineInstr *MIBLO, *MIBHI; + + // HACK: We shouldn't have instances of this instruction + // where src==dest because the instruction itself is + // marked earlyclobber. We do however get this instruction when + // loading from stack slots where the earlyclobber isn't useful. + // + // In this case, just use a temporary register. + if (DstReg == SrcReg) { + RegScavenger RS; + + RS.enterBasicBlock(MBB); + RS.forward(MBBI); + + BitVector Candidates = + TRI->getAllocatableSet + (*MBB.getParent(), &AVR::GPR8RegClass); + + // Exclude all the registers being used by the instruction. + for (MachineOperand &MO : MI.operands()) { + if (MO.isReg() && MO.getReg() != 0 && !MO.isDef() && + !TargetRegisterInfo::isVirtualRegister(MO.getReg())) + Candidates.reset(MO.getReg()); + } + + BitVector Available = RS.getRegsAvailable(&AVR::GPR8RegClass); + Available &= Candidates; + + signed TmpReg = Available.find_first(); + assert(TmpReg != -1 && "ran out of registers"); + + MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(TmpReg, RegState::Define) + .addReg(SrcReg) + .addImm(Imm); + + buildMI(MBB, MBBI, AVR::MOVRdRr).addReg(DstLoReg).addReg(TmpReg); + + MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(TmpReg, RegState::Define) + .addReg(SrcReg, getKillRegState(SrcIsKill)) + .addImm(Imm + 1); + + buildMI(MBB, MBBI, AVR::MOVRdRr).addReg(DstHiReg).addReg(TmpReg); + } else { + MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg) + .addImm(Imm); + + MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg, getKillRegState(SrcIsKill)) + .addImm(Imm + 1); + } + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::LPMWRdZ>(Block &MBB, BlockIt MBBI) { + llvm_unreachable("wide LPM is unimplemented"); +} + +template <> +bool AVRExpandPseudo::expand<AVR::LPMWRdZPi>(Block &MBB, BlockIt MBBI) { + llvm_unreachable("wide LPMPi is unimplemented"); +} + +template<typename Func> +bool AVRExpandPseudo::expandAtomic(Block &MBB, BlockIt MBBI, Func f) { + // Remove the pseudo instruction. + MachineInstr &MI = *MBBI; + + // Store the SREG. + buildMI(MBB, MBBI, AVR::INRdA) + .addReg(SCRATCH_REGISTER, RegState::Define) + .addImm(SREG_ADDR); + + // Disable exceptions. + buildMI(MBB, MBBI, AVR::BCLRs).addImm(7); // CLI + + f(MI); + + // Restore the status reg. + buildMI(MBB, MBBI, AVR::OUTARr) + .addImm(SREG_ADDR) + .addReg(SCRATCH_REGISTER); + + MI.eraseFromParent(); + return true; +} + +template<typename Func> +bool AVRExpandPseudo::expandAtomicBinaryOp(unsigned Opcode, + Block &MBB, + BlockIt MBBI, + Func f) { + return expandAtomic(MBB, MBBI, [&](MachineInstr &MI) { + auto Op1 = MI.getOperand(0); + auto Op2 = MI.getOperand(1); + + MachineInstr &NewInst = *buildMI(MBB, MBBI, Opcode) + .addOperand(Op1).addOperand(Op2) + .getInstr(); + f(NewInst); + }); +} + +bool AVRExpandPseudo::expandAtomicBinaryOp(unsigned Opcode, + Block &MBB, + BlockIt MBBI) { + return expandAtomicBinaryOp(Opcode, MBB, MBBI, [](MachineInstr &MI) {}); +} + +bool AVRExpandPseudo::expandAtomicArithmeticOp(unsigned Width, + unsigned ArithOpcode, + Block &MBB, + BlockIt MBBI) { + return expandAtomic(MBB, MBBI, [&](MachineInstr &MI) { + auto Op1 = MI.getOperand(0); + auto Op2 = MI.getOperand(1); + + unsigned LoadOpcode = (Width == 8) ? AVR::LDRdPtr : AVR::LDWRdPtr; + unsigned StoreOpcode = (Width == 8) ? AVR::STPtrRr : AVR::STWPtrRr; + + // Create the load + buildMI(MBB, MBBI, LoadOpcode).addOperand(Op1).addOperand(Op2); + + // Create the arithmetic op + buildMI(MBB, MBBI, ArithOpcode) + .addOperand(Op1).addOperand(Op1) + .addOperand(Op2); + + // Create the store + buildMI(MBB, MBBI, StoreOpcode).addOperand(Op2).addOperand(Op1); + }); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoad8>(Block &MBB, BlockIt MBBI) { + return expandAtomicBinaryOp(AVR::LDRdPtr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoad16>(Block &MBB, BlockIt MBBI) { + return expandAtomicBinaryOp(AVR::LDWRdPtr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicStore8>(Block &MBB, BlockIt MBBI) { + return expandAtomicBinaryOp(AVR::STPtrRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicStore16>(Block &MBB, BlockIt MBBI) { + return expandAtomicBinaryOp(AVR::STWPtrRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadAdd8>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(8, AVR::ADDRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadAdd16>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(16, AVR::ADDWRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadSub8>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(8, AVR::SUBRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadSub16>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(16, AVR::SUBWRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadAnd8>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(8, AVR::ANDRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadAnd16>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(16, AVR::ANDWRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadOr8>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(8, AVR::ORRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadOr16>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(16, AVR::ORWRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadXor8>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(8, AVR::EORRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadXor16>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(16, AVR::EORWRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicFence>(Block &MBB, BlockIt MBBI) { + // On AVR, there is only one core and so atomic fences do nothing. + MBBI->eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::STSWKRr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg; + unsigned SrcReg = MI.getOperand(1).getReg(); + bool SrcIsKill = MI.getOperand(1).isKill(); + OpLo = AVR::STSKRr; + OpHi = AVR::STSKRr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + // Write the high byte first in case this address belongs to a special + // I/O address with a special temporary register. + auto MIBHI = buildMI(MBB, MBBI, OpHi); + auto MIBLO = buildMI(MBB, MBBI, OpLo); + + switch (MI.getOperand(0).getType()) { + case MachineOperand::MO_GlobalAddress: { + const GlobalValue *GV = MI.getOperand(0).getGlobal(); + int64_t Offs = MI.getOperand(0).getOffset(); + unsigned TF = MI.getOperand(0).getTargetFlags(); + + MIBLO.addGlobalAddress(GV, Offs, TF); + MIBHI.addGlobalAddress(GV, Offs + 1, TF); + break; + } + case MachineOperand::MO_Immediate: { + unsigned Imm = MI.getOperand(0).getImm(); + + MIBLO.addImm(Imm); + MIBHI.addImm(Imm + 1); + break; + } + default: + llvm_unreachable("Unknown operand type!"); + } + + MIBLO.addReg(SrcLoReg, getKillRegState(SrcIsKill)); + MIBHI.addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::STWPtrRr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool DstIsKill = MI.getOperand(0).isKill(); + bool SrcIsKill = MI.getOperand(1).isKill(); + OpLo = AVR::STPtrRr; + OpHi = AVR::STDPtrQRr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + //:TODO: need to reverse this order like inw and stsw? + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstReg) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstReg, getKillRegState(DstIsKill)) + .addImm(1) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::STWPtrPiRr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(2).getReg(); + unsigned Imm = MI.getOperand(3).getImm(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(2).isKill(); + OpLo = AVR::STPtrPiRr; + OpHi = AVR::STPtrPiRr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same"); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstReg, RegState::Define) + .addReg(DstReg, RegState::Kill) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)) + .addImm(Imm); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstReg, RegState::Kill) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)) + .addImm(Imm); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::STWPtrPdRr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(2).getReg(); + unsigned Imm = MI.getOperand(3).getImm(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(2).isKill(); + OpLo = AVR::STPtrPdRr; + OpHi = AVR::STPtrPdRr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same"); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstReg, RegState::Define) + .addReg(DstReg, RegState::Kill) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)) + .addImm(Imm); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstReg, RegState::Kill) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)) + .addImm(Imm); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::STDWPtrQRr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(2).getReg(); + unsigned Imm = MI.getOperand(1).getImm(); + bool DstIsKill = MI.getOperand(0).isKill(); + bool SrcIsKill = MI.getOperand(2).isKill(); + OpLo = AVR::STDPtrQRr; + OpHi = AVR::STDPtrQRr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + assert(Imm <= 63 && "Offset is out of range"); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstReg) + .addImm(Imm) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstReg, getKillRegState(DstIsKill)) + .addImm(Imm + 1) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::INWRdA>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned Imm = MI.getOperand(1).getImm(); + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + OpLo = AVR::INRdA; + OpHi = AVR::INRdA; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + assert(Imm <= 63 && "Address is out of range"); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addImm(Imm); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addImm(Imm + 1); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::OUTWARr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg; + unsigned Imm = MI.getOperand(0).getImm(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool SrcIsKill = MI.getOperand(1).isKill(); + OpLo = AVR::OUTARr; + OpHi = AVR::OUTARr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + assert(Imm <= 63 && "Address is out of range"); + + // 16 bit I/O writes need the high byte first + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addImm(Imm + 1) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addImm(Imm) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::PUSHWRr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg; + unsigned SrcReg = MI.getOperand(0).getReg(); + bool SrcIsKill = MI.getOperand(0).isKill(); + unsigned Flags = MI.getFlags(); + OpLo = AVR::PUSHRr; + OpHi = AVR::PUSHRr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + // Low part + buildMI(MBB, MBBI, OpLo) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)) + .setMIFlags(Flags); + + // High part + buildMI(MBB, MBBI, OpHi) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)) + .setMIFlags(Flags); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::POPWRd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned Flags = MI.getFlags(); + OpLo = AVR::POPRd; + OpHi = AVR::POPRd; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + buildMI(MBB, MBBI, OpHi, DstHiReg).setMIFlags(Flags); // High + buildMI(MBB, MBBI, OpLo, DstLoReg).setMIFlags(Flags); // Low + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::LSLWRd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + OpLo = AVR::LSLRd; + OpHi = AVR::ROLRd; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + // Low part + buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + + if (ImpIsDead) + MIBHI->getOperand(2).setIsDead(); + + // SREG is always implicitly killed + MIBHI->getOperand(3).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::LSRWRd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + OpLo = AVR::RORRd; + OpHi = AVR::LSRRd; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + // High part + buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)); + + if (ImpIsDead) + MIBLO->getOperand(2).setIsDead(); + + // SREG is always implicitly killed + MIBLO->getOperand(3).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::RORWRd>(Block &MBB, BlockIt MBBI) { + llvm_unreachable("RORW unimplemented"); + return false; +} + +template <> +bool AVRExpandPseudo::expand<AVR::ROLWRd>(Block &MBB, BlockIt MBBI) { + llvm_unreachable("ROLW unimplemented"); + return false; +} + +template <> +bool AVRExpandPseudo::expand<AVR::ASRWRd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + OpLo = AVR::RORRd; + OpHi = AVR::ASRRd; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + // High part + buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)); + + if (ImpIsDead) + MIBLO->getOperand(2).setIsDead(); + + // SREG is always implicitly killed + MIBLO->getOperand(3).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> bool AVRExpandPseudo::expand<AVR::SEXT>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned DstLoReg, DstHiReg; + // sext R17:R16, R17 + // mov r16, r17 + // lsl r17 + // sbc r17, r17 + // sext R17:R16, R13 + // mov r16, r13 + // mov r17, r13 + // lsl r17 + // sbc r17, r17 + // sext R17:R16, R16 + // mov r17, r16 + // lsl r17 + // sbc r17, r17 + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + if (SrcReg != DstLoReg) { + auto MOV = buildMI(MBB, MBBI, AVR::MOVRdRr) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg); + + if (SrcReg == DstHiReg) { + MOV->getOperand(1).setIsKill(); + } + } + + if (SrcReg != DstHiReg) { + buildMI(MBB, MBBI, AVR::MOVRdRr) + .addReg(DstHiReg, RegState::Define) + .addReg(SrcReg, getKillRegState(SrcIsKill)); + } + + buildMI(MBB, MBBI, AVR::LSLRd) + .addReg(DstHiReg, RegState::Define) + .addReg(DstHiReg, RegState::Kill); + + auto SBC = buildMI(MBB, MBBI, AVR::SBCRdRr) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, RegState::Kill) + .addReg(DstHiReg, RegState::Kill); + + if (ImpIsDead) + SBC->getOperand(3).setIsDead(); + + // SREG is always implicitly killed + SBC->getOperand(4).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> bool AVRExpandPseudo::expand<AVR::ZEXT>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned DstLoReg, DstHiReg; + // zext R25:R24, R20 + // mov R24, R20 + // eor R25, R25 + // zext R25:R24, R24 + // eor R25, R25 + // zext R25:R24, R25 + // mov R24, R25 + // eor R25, R25 + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + if (SrcReg != DstLoReg) { + buildMI(MBB, MBBI, AVR::MOVRdRr) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg, getKillRegState(SrcIsKill)); + } + + auto EOR = buildMI(MBB, MBBI, AVR::EORRdRr) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, RegState::Kill) + .addReg(DstHiReg, RegState::Kill); + + if (ImpIsDead) + EOR->getOperand(3).setIsDead(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::SPREAD>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + unsigned Flags = MI.getFlags(); + OpLo = AVR::INRdA; + OpHi = AVR::INRdA; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + // Low part + buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addImm(0x3d) + .setMIFlags(Flags); + + // High part + buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addImm(0x3e) + .setMIFlags(Flags); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::SPWRITE>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned SrcLoReg, SrcHiReg; + unsigned SrcReg = MI.getOperand(1).getReg(); + bool SrcIsKill = MI.getOperand(1).isKill(); + unsigned Flags = MI.getFlags(); + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + buildMI(MBB, MBBI, AVR::INRdA) + .addReg(AVR::R0, RegState::Define) + .addImm(SREG_ADDR) + .setMIFlags(Flags); + + buildMI(MBB, MBBI, AVR::BCLRs).addImm(0x07).setMIFlags(Flags); + + buildMI(MBB, MBBI, AVR::OUTARr) + .addImm(0x3e) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)) + .setMIFlags(Flags); + + buildMI(MBB, MBBI, AVR::OUTARr) + .addImm(SREG_ADDR) + .addReg(AVR::R0, RegState::Kill) + .setMIFlags(Flags); + + buildMI(MBB, MBBI, AVR::OUTARr) + .addImm(0x3d) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)) + .setMIFlags(Flags); + + MI.eraseFromParent(); + return true; +} + +bool AVRExpandPseudo::expandMI(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + int Opcode = MBBI->getOpcode(); + +#define EXPAND(Op) \ + case Op: \ + return expand<Op>(MBB, MI) + + switch (Opcode) { + EXPAND(AVR::ADDWRdRr); + EXPAND(AVR::ADCWRdRr); + EXPAND(AVR::SUBWRdRr); + EXPAND(AVR::SUBIWRdK); + EXPAND(AVR::SBCWRdRr); + EXPAND(AVR::SBCIWRdK); + EXPAND(AVR::ANDWRdRr); + EXPAND(AVR::ANDIWRdK); + EXPAND(AVR::ORWRdRr); + EXPAND(AVR::ORIWRdK); + EXPAND(AVR::EORWRdRr); + EXPAND(AVR::COMWRd); + EXPAND(AVR::CPWRdRr); + EXPAND(AVR::CPCWRdRr); + EXPAND(AVR::LDIWRdK); + EXPAND(AVR::LDSWRdK); + EXPAND(AVR::LDWRdPtr); + EXPAND(AVR::LDWRdPtrPi); + EXPAND(AVR::LDWRdPtrPd); + case AVR::LDDWRdYQ: //:FIXME: remove this once PR13375 gets fixed + EXPAND(AVR::LDDWRdPtrQ); + EXPAND(AVR::LPMWRdZ); + EXPAND(AVR::LPMWRdZPi); + EXPAND(AVR::AtomicLoad8); + EXPAND(AVR::AtomicLoad16); + EXPAND(AVR::AtomicStore8); + EXPAND(AVR::AtomicStore16); + EXPAND(AVR::AtomicLoadAdd8); + EXPAND(AVR::AtomicLoadAdd16); + EXPAND(AVR::AtomicLoadSub8); + EXPAND(AVR::AtomicLoadSub16); + EXPAND(AVR::AtomicLoadAnd8); + EXPAND(AVR::AtomicLoadAnd16); + EXPAND(AVR::AtomicLoadOr8); + EXPAND(AVR::AtomicLoadOr16); + EXPAND(AVR::AtomicLoadXor8); + EXPAND(AVR::AtomicLoadXor16); + EXPAND(AVR::AtomicFence); + EXPAND(AVR::STSWKRr); + EXPAND(AVR::STWPtrRr); + EXPAND(AVR::STWPtrPiRr); + EXPAND(AVR::STWPtrPdRr); + EXPAND(AVR::STDWPtrQRr); + EXPAND(AVR::INWRdA); + EXPAND(AVR::OUTWARr); + EXPAND(AVR::PUSHWRr); + EXPAND(AVR::POPWRd); + EXPAND(AVR::LSLWRd); + EXPAND(AVR::LSRWRd); + EXPAND(AVR::RORWRd); + EXPAND(AVR::ROLWRd); + EXPAND(AVR::ASRWRd); + EXPAND(AVR::SEXT); + EXPAND(AVR::ZEXT); + EXPAND(AVR::SPREAD); + EXPAND(AVR::SPWRITE); + } +#undef EXPAND + return false; +} + +} // end of anonymous namespace + +INITIALIZE_PASS(AVRExpandPseudo, "avr-expand-pseudo", + AVR_EXPAND_PSEUDO_NAME, false, false) +namespace llvm { + +FunctionPass *createAVRExpandPseudoPass() { return new AVRExpandPseudo(); } + +} // end of namespace llvm diff --git a/contrib/llvm/lib/Target/AVR/AVRFrameLowering.cpp b/contrib/llvm/lib/Target/AVR/AVRFrameLowering.cpp new file mode 100644 index 0000000..b8cb221 --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/AVRFrameLowering.cpp @@ -0,0 +1,538 @@ +//===-- AVRFrameLowering.cpp - AVR Frame Information ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the AVR implementation of TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#include "AVRFrameLowering.h" + +#include "AVR.h" +#include "AVRInstrInfo.h" +#include "AVRMachineFunctionInfo.h" +#include "AVRTargetMachine.h" +#include "MCTargetDesc/AVRMCTargetDesc.h" + +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/Function.h" + +#include <vector> + +namespace llvm { + +AVRFrameLowering::AVRFrameLowering() + : TargetFrameLowering(TargetFrameLowering::StackGrowsDown, 1, -2) {} + +bool AVRFrameLowering::canSimplifyCallFramePseudos( + const MachineFunction &MF) const { + // Always simplify call frame pseudo instructions, even when + // hasReservedCallFrame is false. + return true; +} + +bool AVRFrameLowering::hasReservedCallFrame(const MachineFunction &MF) const { + // Reserve call frame memory in function prologue under the following + // conditions: + // - Y pointer is reserved to be the frame pointer. + // - The function does not contain variable sized objects. + + const MachineFrameInfo &MFI = MF.getFrameInfo(); + return hasFP(MF) && !MFI.hasVarSizedObjects(); +} + +void AVRFrameLowering::emitPrologue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + MachineBasicBlock::iterator MBBI = MBB.begin(); + CallingConv::ID CallConv = MF.getFunction()->getCallingConv(); + DebugLoc DL = (MBBI != MBB.end()) ? MBBI->getDebugLoc() : DebugLoc(); + const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>(); + const AVRInstrInfo &TII = *STI.getInstrInfo(); + + // Interrupt handlers re-enable interrupts in function entry. + if (CallConv == CallingConv::AVR_INTR) { + BuildMI(MBB, MBBI, DL, TII.get(AVR::BSETs)) + .addImm(0x07) + .setMIFlag(MachineInstr::FrameSetup); + } + + // Emit special prologue code to save R1, R0 and SREG in interrupt/signal + // handlers before saving any other registers. + if (CallConv == CallingConv::AVR_INTR || + CallConv == CallingConv::AVR_SIGNAL) { + BuildMI(MBB, MBBI, DL, TII.get(AVR::PUSHWRr)) + .addReg(AVR::R1R0, RegState::Kill) + .setMIFlag(MachineInstr::FrameSetup); + BuildMI(MBB, MBBI, DL, TII.get(AVR::INRdA), AVR::R0) + .addImm(0x3f) + .setMIFlag(MachineInstr::FrameSetup); + BuildMI(MBB, MBBI, DL, TII.get(AVR::PUSHRr)) + .addReg(AVR::R0, RegState::Kill) + .setMIFlag(MachineInstr::FrameSetup); + BuildMI(MBB, MBBI, DL, TII.get(AVR::EORRdRr)) + .addReg(AVR::R0, RegState::Define) + .addReg(AVR::R0, RegState::Kill) + .addReg(AVR::R0, RegState::Kill) + .setMIFlag(MachineInstr::FrameSetup); + } + + // Early exit if the frame pointer is not needed in this function. + if (!hasFP(MF)) { + return; + } + + const MachineFrameInfo &MFI = MF.getFrameInfo(); + const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>(); + unsigned FrameSize = MFI.getStackSize() - AFI->getCalleeSavedFrameSize(); + + // Skip the callee-saved push instructions. + while ( + (MBBI != MBB.end()) && MBBI->getFlag(MachineInstr::FrameSetup) && + (MBBI->getOpcode() == AVR::PUSHRr || MBBI->getOpcode() == AVR::PUSHWRr)) { + ++MBBI; + } + + // Update Y with the new base value. + BuildMI(MBB, MBBI, DL, TII.get(AVR::SPREAD), AVR::R29R28) + .addReg(AVR::SP) + .setMIFlag(MachineInstr::FrameSetup); + + // Mark the FramePtr as live-in in every block except the entry. + for (MachineFunction::iterator I = std::next(MF.begin()), E = MF.end(); + I != E; ++I) { + I->addLiveIn(AVR::R29R28); + } + + if (!FrameSize) { + return; + } + + // Reserve the necessary frame memory by doing FP -= <size>. + unsigned Opcode = (isUInt<6>(FrameSize)) ? AVR::SBIWRdK : AVR::SUBIWRdK; + + MachineInstr *MI = BuildMI(MBB, MBBI, DL, TII.get(Opcode), AVR::R29R28) + .addReg(AVR::R29R28, RegState::Kill) + .addImm(FrameSize) + .setMIFlag(MachineInstr::FrameSetup); + // The SREG implicit def is dead. + MI->getOperand(3).setIsDead(); + + // Write back R29R28 to SP and temporarily disable interrupts. + BuildMI(MBB, MBBI, DL, TII.get(AVR::SPWRITE), AVR::SP) + .addReg(AVR::R29R28) + .setMIFlag(MachineInstr::FrameSetup); +} + +void AVRFrameLowering::emitEpilogue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + CallingConv::ID CallConv = MF.getFunction()->getCallingConv(); + bool isHandler = (CallConv == CallingConv::AVR_INTR || + CallConv == CallingConv::AVR_SIGNAL); + + // Early exit if the frame pointer is not needed in this function except for + // signal/interrupt handlers where special code generation is required. + if (!hasFP(MF) && !isHandler) { + return; + } + + MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); + assert(MBBI->getDesc().isReturn() && + "Can only insert epilog into returning blocks"); + + DebugLoc DL = MBBI->getDebugLoc(); + const MachineFrameInfo &MFI = MF.getFrameInfo(); + const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>(); + unsigned FrameSize = MFI.getStackSize() - AFI->getCalleeSavedFrameSize(); + const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>(); + const AVRInstrInfo &TII = *STI.getInstrInfo(); + + // Emit special epilogue code to restore R1, R0 and SREG in interrupt/signal + // handlers at the very end of the function, just before reti. + if (isHandler) { + BuildMI(MBB, MBBI, DL, TII.get(AVR::POPRd), AVR::R0); + BuildMI(MBB, MBBI, DL, TII.get(AVR::OUTARr)) + .addImm(0x3f) + .addReg(AVR::R0, RegState::Kill); + BuildMI(MBB, MBBI, DL, TII.get(AVR::POPWRd), AVR::R1R0); + } + + // Early exit if there is no need to restore the frame pointer. + if (!FrameSize) { + return; + } + + // Skip the callee-saved pop instructions. + while (MBBI != MBB.begin()) { + MachineBasicBlock::iterator PI = std::prev(MBBI); + int Opc = PI->getOpcode(); + + if (Opc != AVR::POPRd && Opc != AVR::POPWRd && !PI->isTerminator()) { + break; + } + + --MBBI; + } + + unsigned Opcode; + + // Select the optimal opcode depending on how big it is. + if (isUInt<6>(FrameSize)) { + Opcode = AVR::ADIWRdK; + } else { + Opcode = AVR::SUBIWRdK; + FrameSize = -FrameSize; + } + + // Restore the frame pointer by doing FP += <size>. + MachineInstr *MI = BuildMI(MBB, MBBI, DL, TII.get(Opcode), AVR::R29R28) + .addReg(AVR::R29R28, RegState::Kill) + .addImm(FrameSize); + // The SREG implicit def is dead. + MI->getOperand(3).setIsDead(); + + // Write back R29R28 to SP and temporarily disable interrupts. + BuildMI(MBB, MBBI, DL, TII.get(AVR::SPWRITE), AVR::SP) + .addReg(AVR::R29R28, RegState::Kill); +} + +// Return true if the specified function should have a dedicated frame +// pointer register. This is true if the function meets any of the following +// conditions: +// - a register has been spilled +// - has allocas +// - input arguments are passed using the stack +// +// Notice that strictly this is not a frame pointer because it contains SP after +// frame allocation instead of having the original SP in function entry. +bool AVRFrameLowering::hasFP(const MachineFunction &MF) const { + const AVRMachineFunctionInfo *FuncInfo = MF.getInfo<AVRMachineFunctionInfo>(); + + return (FuncInfo->getHasSpills() || FuncInfo->getHasAllocas() || + FuncInfo->getHasStackArgs()); +} + +bool AVRFrameLowering::spillCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + const std::vector<CalleeSavedInfo> &CSI, + const TargetRegisterInfo *TRI) const { + if (CSI.empty()) { + return false; + } + + unsigned CalleeFrameSize = 0; + DebugLoc DL = MBB.findDebugLoc(MI); + MachineFunction &MF = *MBB.getParent(); + const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>(); + const TargetInstrInfo &TII = *STI.getInstrInfo(); + AVRMachineFunctionInfo *AVRFI = MF.getInfo<AVRMachineFunctionInfo>(); + + for (unsigned i = CSI.size(); i != 0; --i) { + unsigned Reg = CSI[i - 1].getReg(); + bool IsNotLiveIn = !MBB.isLiveIn(Reg); + + assert(TRI->getMinimalPhysRegClass(Reg)->getSize() == 1 && + "Invalid register size"); + + // Add the callee-saved register as live-in only if it is not already a + // live-in register, this usually happens with arguments that are passed + // through callee-saved registers. + if (IsNotLiveIn) { + MBB.addLiveIn(Reg); + } + + // Do not kill the register when it is an input argument. + BuildMI(MBB, MI, DL, TII.get(AVR::PUSHRr)) + .addReg(Reg, getKillRegState(IsNotLiveIn)) + .setMIFlag(MachineInstr::FrameSetup); + ++CalleeFrameSize; + } + + AVRFI->setCalleeSavedFrameSize(CalleeFrameSize); + + return true; +} + +bool AVRFrameLowering::restoreCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + const std::vector<CalleeSavedInfo> &CSI, + const TargetRegisterInfo *TRI) const { + if (CSI.empty()) { + return false; + } + + DebugLoc DL = MBB.findDebugLoc(MI); + const MachineFunction &MF = *MBB.getParent(); + const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>(); + const TargetInstrInfo &TII = *STI.getInstrInfo(); + + for (const CalleeSavedInfo &CCSI : CSI) { + unsigned Reg = CCSI.getReg(); + + assert(TRI->getMinimalPhysRegClass(Reg)->getSize() == 1 && + "Invalid register size"); + + BuildMI(MBB, MI, DL, TII.get(AVR::POPRd), Reg); + } + + return true; +} + +/// Replace pseudo store instructions that pass arguments through the stack with +/// real instructions. If insertPushes is true then all instructions are +/// replaced with push instructions, otherwise regular std instructions are +/// inserted. +static void fixStackStores(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const TargetInstrInfo &TII, bool insertPushes) { + const AVRSubtarget &STI = MBB.getParent()->getSubtarget<AVRSubtarget>(); + const TargetRegisterInfo &TRI = *STI.getRegisterInfo(); + + // Iterate through the BB until we hit a call instruction or we reach the end. + for (auto I = MI, E = MBB.end(); I != E && !I->isCall();) { + MachineBasicBlock::iterator NextMI = std::next(I); + MachineInstr &MI = *I; + unsigned Opcode = I->getOpcode(); + + // Only care of pseudo store instructions where SP is the base pointer. + if (Opcode != AVR::STDSPQRr && Opcode != AVR::STDWSPQRr) { + I = NextMI; + continue; + } + + assert(MI.getOperand(0).getReg() == AVR::SP && + "Invalid register, should be SP!"); + if (insertPushes) { + // Replace this instruction with a push. + unsigned SrcReg = MI.getOperand(2).getReg(); + bool SrcIsKill = MI.getOperand(2).isKill(); + + // We can't use PUSHWRr here because when expanded the order of the new + // instructions are reversed from what we need. Perform the expansion now. + if (Opcode == AVR::STDWSPQRr) { + BuildMI(MBB, I, MI.getDebugLoc(), TII.get(AVR::PUSHRr)) + .addReg(TRI.getSubReg(SrcReg, AVR::sub_hi), + getKillRegState(SrcIsKill)); + BuildMI(MBB, I, MI.getDebugLoc(), TII.get(AVR::PUSHRr)) + .addReg(TRI.getSubReg(SrcReg, AVR::sub_lo), + getKillRegState(SrcIsKill)); + } else { + BuildMI(MBB, I, MI.getDebugLoc(), TII.get(AVR::PUSHRr)) + .addReg(SrcReg, getKillRegState(SrcIsKill)); + } + + MI.eraseFromParent(); + I = NextMI; + continue; + } + + // Replace this instruction with a regular store. Use Y as the base + // pointer since it is guaranteed to contain a copy of SP. + unsigned STOpc = + (Opcode == AVR::STDWSPQRr) ? AVR::STDWPtrQRr : AVR::STDPtrQRr; + + MI.setDesc(TII.get(STOpc)); + MI.getOperand(0).setReg(AVR::R29R28); + + I = NextMI; + } +} + +MachineBasicBlock::iterator AVRFrameLowering::eliminateCallFramePseudoInstr( + MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI) const { + const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>(); + const TargetFrameLowering &TFI = *STI.getFrameLowering(); + const AVRInstrInfo &TII = *STI.getInstrInfo(); + + // There is nothing to insert when the call frame memory is allocated during + // function entry. Delete the call frame pseudo and replace all pseudo stores + // with real store instructions. + if (TFI.hasReservedCallFrame(MF)) { + fixStackStores(MBB, MI, TII, false); + return MBB.erase(MI); + } + + DebugLoc DL = MI->getDebugLoc(); + unsigned int Opcode = MI->getOpcode(); + int Amount = MI->getOperand(0).getImm(); + + // Adjcallstackup does not need to allocate stack space for the call, instead + // we insert push instructions that will allocate the necessary stack. + // For adjcallstackdown we convert it into an 'adiw reg, <amt>' handling + // the read and write of SP in I/O space. + if (Amount != 0) { + assert(TFI.getStackAlignment() == 1 && "Unsupported stack alignment"); + + if (Opcode == TII.getCallFrameSetupOpcode()) { + fixStackStores(MBB, MI, TII, true); + } else { + assert(Opcode == TII.getCallFrameDestroyOpcode()); + + // Select the best opcode to adjust SP based on the offset size. + unsigned addOpcode; + if (isUInt<6>(Amount)) { + addOpcode = AVR::ADIWRdK; + } else { + addOpcode = AVR::SUBIWRdK; + Amount = -Amount; + } + + // Build the instruction sequence. + BuildMI(MBB, MI, DL, TII.get(AVR::SPREAD), AVR::R31R30).addReg(AVR::SP); + + MachineInstr *New = BuildMI(MBB, MI, DL, TII.get(addOpcode), AVR::R31R30) + .addReg(AVR::R31R30, RegState::Kill) + .addImm(Amount); + New->getOperand(3).setIsDead(); + + BuildMI(MBB, MI, DL, TII.get(AVR::SPWRITE), AVR::SP) + .addReg(AVR::R31R30, RegState::Kill); + } + } + + return MBB.erase(MI); +} + +void AVRFrameLowering::determineCalleeSaves(MachineFunction &MF, + BitVector &SavedRegs, + RegScavenger *RS) const { + TargetFrameLowering::determineCalleeSaves(MF, SavedRegs, RS); + + // Spill register Y when it is used as the frame pointer. + if (hasFP(MF)) { + SavedRegs.set(AVR::R29R28); + SavedRegs.set(AVR::R29); + SavedRegs.set(AVR::R28); + } +} +/// The frame analyzer pass. +/// +/// Scans the function for allocas and used arguments +/// that are passed through the stack. +struct AVRFrameAnalyzer : public MachineFunctionPass { + static char ID; + AVRFrameAnalyzer() : MachineFunctionPass(ID) {} + + bool runOnMachineFunction(MachineFunction &MF) { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + AVRMachineFunctionInfo *FuncInfo = MF.getInfo<AVRMachineFunctionInfo>(); + + // If there are no fixed frame indexes during this stage it means there + // are allocas present in the function. + if (MFI.getNumObjects() != MFI.getNumFixedObjects()) { + // Check for the type of allocas present in the function. We only care + // about fixed size allocas so do not give false positives if only + // variable sized allocas are present. + for (unsigned i = 0, e = MFI.getObjectIndexEnd(); i != e; ++i) { + // Variable sized objects have size 0. + if (MFI.getObjectSize(i)) { + FuncInfo->setHasAllocas(true); + break; + } + } + } + + // If there are fixed frame indexes present, scan the function to see if + // they are really being used. + if (MFI.getNumFixedObjects() == 0) { + return false; + } + + // Ok fixed frame indexes present, now scan the function to see if they + // are really being used, otherwise we can ignore them. + for (const MachineBasicBlock &BB : MF) { + for (const MachineInstr &MI : BB) { + int Opcode = MI.getOpcode(); + + if ((Opcode != AVR::LDDRdPtrQ) && (Opcode != AVR::LDDWRdPtrQ) && + (Opcode != AVR::STDPtrQRr) && (Opcode != AVR::STDWPtrQRr)) { + continue; + } + + for (const MachineOperand &MO : MI.operands()) { + if (!MO.isFI()) { + continue; + } + + if (MFI.isFixedObjectIndex(MO.getIndex())) { + FuncInfo->setHasStackArgs(true); + return false; + } + } + } + } + + return false; + } + + StringRef getPassName() const { return "AVR Frame Analyzer"; } +}; + +char AVRFrameAnalyzer::ID = 0; + +/// Creates instance of the frame analyzer pass. +FunctionPass *createAVRFrameAnalyzerPass() { return new AVRFrameAnalyzer(); } + +/// Create the Dynalloca Stack Pointer Save/Restore pass. +/// Insert a copy of SP before allocating the dynamic stack memory and restore +/// it in function exit to restore the original SP state. This avoids the need +/// of reserving a register pair for a frame pointer. +struct AVRDynAllocaSR : public MachineFunctionPass { + static char ID; + AVRDynAllocaSR() : MachineFunctionPass(ID) {} + + bool runOnMachineFunction(MachineFunction &MF) { + // Early exit when there are no variable sized objects in the function. + if (!MF.getFrameInfo().hasVarSizedObjects()) { + return false; + } + + const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>(); + const TargetInstrInfo &TII = *STI.getInstrInfo(); + MachineBasicBlock &EntryMBB = MF.front(); + MachineBasicBlock::iterator MBBI = EntryMBB.begin(); + DebugLoc DL = EntryMBB.findDebugLoc(MBBI); + + unsigned SPCopy = + MF.getRegInfo().createVirtualRegister(&AVR::DREGSRegClass); + + // Create a copy of SP in function entry before any dynallocas are + // inserted. + BuildMI(EntryMBB, MBBI, DL, TII.get(AVR::COPY), SPCopy).addReg(AVR::SP); + + // Restore SP in all exit basic blocks. + for (MachineBasicBlock &MBB : MF) { + // If last instruction is a return instruction, add a restore copy. + if (!MBB.empty() && MBB.back().isReturn()) { + MBBI = MBB.getLastNonDebugInstr(); + DL = MBBI->getDebugLoc(); + BuildMI(MBB, MBBI, DL, TII.get(AVR::COPY), AVR::SP) + .addReg(SPCopy, RegState::Kill); + } + } + + return true; + } + + StringRef getPassName() const { + return "AVR dynalloca stack pointer save/restore"; + } +}; + +char AVRDynAllocaSR::ID = 0; + +/// createAVRDynAllocaSRPass - returns an instance of the dynalloca stack +/// pointer save/restore pass. +FunctionPass *createAVRDynAllocaSRPass() { return new AVRDynAllocaSR(); } + +} // end of namespace llvm + diff --git a/contrib/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp b/contrib/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp new file mode 100644 index 0000000..462a7d5 --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp @@ -0,0 +1,565 @@ +//===-- AVRISelDAGToDAG.cpp - A dag to dag inst selector for AVR ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines an instruction selector for the AVR target. +// +//===----------------------------------------------------------------------===// + +#include "AVR.h" +#include "AVRTargetMachine.h" +#include "MCTargetDesc/AVRMCTargetDesc.h" + +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/SelectionDAGISel.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +#define DEBUG_TYPE "avr-isel" + +namespace llvm { + +/// Lowers LLVM IR (in DAG form) to AVR MC instructions (in DAG form). +class AVRDAGToDAGISel : public SelectionDAGISel { +public: + AVRDAGToDAGISel(AVRTargetMachine &TM, CodeGenOpt::Level OptLevel) + : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr) {} + + StringRef getPassName() const override { + return "AVR DAG->DAG Instruction Selection"; + } + + bool runOnMachineFunction(MachineFunction &MF) override; + + bool SelectAddr(SDNode *Op, SDValue N, SDValue &Base, SDValue &Disp); + + bool selectIndexedLoad(SDNode *N); + unsigned selectIndexedProgMemLoad(const LoadSDNode *LD, MVT VT); + + bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintCode, + std::vector<SDValue> &OutOps) override; + +// Include the pieces autogenerated from the target description. +#include "AVRGenDAGISel.inc" + +private: + void Select(SDNode *N) override; + bool trySelect(SDNode *N); + + template <unsigned NodeType> bool select(SDNode *N); + bool selectMultiplication(SDNode *N); + + const AVRSubtarget *Subtarget; +}; + +bool AVRDAGToDAGISel::runOnMachineFunction(MachineFunction &MF) { + Subtarget = &MF.getSubtarget<AVRSubtarget>(); + return SelectionDAGISel::runOnMachineFunction(MF); +} + +bool AVRDAGToDAGISel::SelectAddr(SDNode *Op, SDValue N, SDValue &Base, + SDValue &Disp) { + SDLoc dl(Op); + auto DL = CurDAG->getDataLayout(); + MVT PtrVT = getTargetLowering()->getPointerTy(DL); + + // if the address is a frame index get the TargetFrameIndex. + if (const FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(N)) { + Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), PtrVT); + Disp = CurDAG->getTargetConstant(0, dl, MVT::i8); + + return true; + } + + // Match simple Reg + uimm6 operands. + if (N.getOpcode() != ISD::ADD && N.getOpcode() != ISD::SUB && + !CurDAG->isBaseWithConstantOffset(N)) { + return false; + } + + if (const ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(N.getOperand(1))) { + int RHSC = (int)RHS->getZExtValue(); + + // Convert negative offsets into positives ones. + if (N.getOpcode() == ISD::SUB) { + RHSC = -RHSC; + } + + // <#Frame index + const> + // Allow folding offsets bigger than 63 so the frame pointer can be used + // directly instead of copying it around by adjusting and restoring it for + // each access. + if (N.getOperand(0).getOpcode() == ISD::FrameIndex) { + int FI = cast<FrameIndexSDNode>(N.getOperand(0))->getIndex(); + + Base = CurDAG->getTargetFrameIndex(FI, PtrVT); + Disp = CurDAG->getTargetConstant(RHSC, dl, MVT::i16); + + return true; + } + + // The value type of the memory instruction determines what is the maximum + // offset allowed. + MVT VT = cast<MemSDNode>(Op)->getMemoryVT().getSimpleVT(); + + // We only accept offsets that fit in 6 bits (unsigned). + if (isUInt<6>(RHSC) && (VT == MVT::i8 || VT == MVT::i16)) { + Base = N.getOperand(0); + Disp = CurDAG->getTargetConstant(RHSC, dl, MVT::i8); + + return true; + } + } + + return false; +} + +bool AVRDAGToDAGISel::selectIndexedLoad(SDNode *N) { + const LoadSDNode *LD = cast<LoadSDNode>(N); + ISD::MemIndexedMode AM = LD->getAddressingMode(); + MVT VT = LD->getMemoryVT().getSimpleVT(); + auto PtrVT = getTargetLowering()->getPointerTy(CurDAG->getDataLayout()); + + // We only care if this load uses a POSTINC or PREDEC mode. + if ((LD->getExtensionType() != ISD::NON_EXTLOAD) || + (AM != ISD::POST_INC && AM != ISD::PRE_DEC)) { + + return false; + } + + unsigned Opcode = 0; + bool isPre = (AM == ISD::PRE_DEC); + int Offs = cast<ConstantSDNode>(LD->getOffset())->getSExtValue(); + + switch (VT.SimpleTy) { + case MVT::i8: { + if ((!isPre && Offs != 1) || (isPre && Offs != -1)) { + return false; + } + + Opcode = (isPre) ? AVR::LDRdPtrPd : AVR::LDRdPtrPi; + break; + } + case MVT::i16: { + if ((!isPre && Offs != 2) || (isPre && Offs != -2)) { + return false; + } + + Opcode = (isPre) ? AVR::LDWRdPtrPd : AVR::LDWRdPtrPi; + break; + } + default: + return false; + } + + SDNode *ResNode = CurDAG->getMachineNode(Opcode, SDLoc(N), VT, + PtrVT, MVT::Other, + LD->getBasePtr(), LD->getChain()); + ReplaceUses(N, ResNode); + CurDAG->RemoveDeadNode(N); + + return true; +} + +unsigned AVRDAGToDAGISel::selectIndexedProgMemLoad(const LoadSDNode *LD, + MVT VT) { + ISD::MemIndexedMode AM = LD->getAddressingMode(); + + // Progmem indexed loads only work in POSTINC mode. + if (LD->getExtensionType() != ISD::NON_EXTLOAD || AM != ISD::POST_INC) { + return 0; + } + + unsigned Opcode = 0; + int Offs = cast<ConstantSDNode>(LD->getOffset())->getSExtValue(); + + switch (VT.SimpleTy) { + case MVT::i8: { + if (Offs != 1) { + return 0; + } + Opcode = AVR::LPMRdZPi; + break; + } + case MVT::i16: { + if (Offs != 2) { + return 0; + } + Opcode = AVR::LPMWRdZPi; + break; + } + default: + return 0; + } + + return Opcode; +} + +bool AVRDAGToDAGISel::SelectInlineAsmMemoryOperand(const SDValue &Op, + unsigned ConstraintCode, + std::vector<SDValue> &OutOps) { + assert((ConstraintCode == InlineAsm::Constraint_m || + ConstraintCode == InlineAsm::Constraint_Q) && + "Unexpected asm memory constraint"); + + MachineRegisterInfo &RI = MF->getRegInfo(); + const AVRSubtarget &STI = MF->getSubtarget<AVRSubtarget>(); + const TargetLowering &TL = *STI.getTargetLowering(); + SDLoc dl(Op); + auto DL = CurDAG->getDataLayout(); + + const RegisterSDNode *RegNode = dyn_cast<RegisterSDNode>(Op); + + // If address operand is of PTRDISPREGS class, all is OK, then. + if (RegNode && + RI.getRegClass(RegNode->getReg()) == &AVR::PTRDISPREGSRegClass) { + OutOps.push_back(Op); + return false; + } + + if (Op->getOpcode() == ISD::FrameIndex) { + SDValue Base, Disp; + + if (SelectAddr(Op.getNode(), Op, Base, Disp)) { + OutOps.push_back(Base); + OutOps.push_back(Disp); + + return false; + } + + return true; + } + + // If Op is add 'register, immediate' and + // register is either virtual register or register of PTRDISPREGSRegClass + if (Op->getOpcode() == ISD::ADD || Op->getOpcode() == ISD::SUB) { + SDValue CopyFromRegOp = Op->getOperand(0); + SDValue ImmOp = Op->getOperand(1); + ConstantSDNode *ImmNode = dyn_cast<ConstantSDNode>(ImmOp); + + unsigned Reg; + bool CanHandleRegImmOpt = true; + + CanHandleRegImmOpt &= ImmNode != 0; + CanHandleRegImmOpt &= ImmNode->getAPIntValue().getZExtValue() < 64; + + if (CopyFromRegOp->getOpcode() == ISD::CopyFromReg) { + RegisterSDNode *RegNode = + cast<RegisterSDNode>(CopyFromRegOp->getOperand(1)); + Reg = RegNode->getReg(); + CanHandleRegImmOpt &= (TargetRegisterInfo::isVirtualRegister(Reg) || + AVR::PTRDISPREGSRegClass.contains(Reg)); + } else { + CanHandleRegImmOpt = false; + } + + // If we detect proper case - correct virtual register class + // if needed and go to another inlineasm operand. + if (CanHandleRegImmOpt) { + SDValue Base, Disp; + + if (RI.getRegClass(Reg) != &AVR::PTRDISPREGSRegClass) { + SDLoc dl(CopyFromRegOp); + + unsigned VReg = RI.createVirtualRegister(&AVR::PTRDISPREGSRegClass); + + SDValue CopyToReg = + CurDAG->getCopyToReg(CopyFromRegOp, dl, VReg, CopyFromRegOp); + + SDValue NewCopyFromRegOp = + CurDAG->getCopyFromReg(CopyToReg, dl, VReg, TL.getPointerTy(DL)); + + Base = NewCopyFromRegOp; + } else { + Base = CopyFromRegOp; + } + + if (ImmNode->getValueType(0) != MVT::i8) { + Disp = CurDAG->getTargetConstant(ImmNode->getAPIntValue().getZExtValue(), dl, MVT::i8); + } else { + Disp = ImmOp; + } + + OutOps.push_back(Base); + OutOps.push_back(Disp); + + return false; + } + } + + // More generic case. + // Create chain that puts Op into pointer register + // and return that register. + unsigned VReg = RI.createVirtualRegister(&AVR::PTRDISPREGSRegClass); + + SDValue CopyToReg = CurDAG->getCopyToReg(Op, dl, VReg, Op); + SDValue CopyFromReg = + CurDAG->getCopyFromReg(CopyToReg, dl, VReg, TL.getPointerTy(DL)); + + OutOps.push_back(CopyFromReg); + + return false; +} + +template <> bool AVRDAGToDAGISel::select<ISD::FrameIndex>(SDNode *N) { + auto DL = CurDAG->getDataLayout(); + + // Convert the frameindex into a temp instruction that will hold the + // effective address of the final stack slot. + int FI = cast<FrameIndexSDNode>(N)->getIndex(); + SDValue TFI = + CurDAG->getTargetFrameIndex(FI, getTargetLowering()->getPointerTy(DL)); + + CurDAG->SelectNodeTo(N, AVR::FRMIDX, + getTargetLowering()->getPointerTy(DL), TFI, + CurDAG->getTargetConstant(0, SDLoc(N), MVT::i16)); + return true; +} + +template <> bool AVRDAGToDAGISel::select<ISD::STORE>(SDNode *N) { + // Use the STD{W}SPQRr pseudo instruction when passing arguments through + // the stack on function calls for further expansion during the PEI phase. + const StoreSDNode *ST = cast<StoreSDNode>(N); + SDValue BasePtr = ST->getBasePtr(); + + // Early exit when the base pointer is a frame index node or a constant. + if (isa<FrameIndexSDNode>(BasePtr) || isa<ConstantSDNode>(BasePtr) || + BasePtr.isUndef()) { + return false; + } + + const RegisterSDNode *RN = dyn_cast<RegisterSDNode>(BasePtr.getOperand(0)); + // Only stores where SP is the base pointer are valid. + if (!RN || (RN->getReg() != AVR::SP)) { + return false; + } + + int CST = (int)cast<ConstantSDNode>(BasePtr.getOperand(1))->getZExtValue(); + SDValue Chain = ST->getChain(); + EVT VT = ST->getValue().getValueType(); + SDLoc DL(N); + SDValue Offset = CurDAG->getTargetConstant(CST, DL, MVT::i16); + SDValue Ops[] = {BasePtr.getOperand(0), Offset, ST->getValue(), Chain}; + unsigned Opc = (VT == MVT::i16) ? AVR::STDWSPQRr : AVR::STDSPQRr; + + SDNode *ResNode = CurDAG->getMachineNode(Opc, DL, MVT::Other, Ops); + + // Transfer memory operands. + MachineSDNode::mmo_iterator MemOp = MF->allocateMemRefsArray(1); + MemOp[0] = ST->getMemOperand(); + cast<MachineSDNode>(ResNode)->setMemRefs(MemOp, MemOp + 1); + + ReplaceUses(SDValue(N, 0), SDValue(ResNode, 0)); + CurDAG->RemoveDeadNode(N); + + return true; +} + +template <> bool AVRDAGToDAGISel::select<ISD::LOAD>(SDNode *N) { + const LoadSDNode *LD = cast<LoadSDNode>(N); + if (!AVR::isProgramMemoryAccess(LD)) { + // Check if the opcode can be converted into an indexed load. + return selectIndexedLoad(N); + } + + assert(Subtarget->hasLPM() && "cannot load from program memory on this mcu"); + + // This is a flash memory load, move the pointer into R31R30 and emit + // the lpm instruction. + MVT VT = LD->getMemoryVT().getSimpleVT(); + SDValue Chain = LD->getChain(); + SDValue Ptr = LD->getBasePtr(); + SDNode *ResNode; + SDLoc DL(N); + + Chain = CurDAG->getCopyToReg(Chain, DL, AVR::R31R30, Ptr, SDValue()); + Ptr = CurDAG->getCopyFromReg(Chain, DL, AVR::R31R30, MVT::i16, + Chain.getValue(1)); + + SDValue RegZ = CurDAG->getRegister(AVR::R31R30, MVT::i16); + + // Check if the opcode can be converted into an indexed load. + if (unsigned LPMOpc = selectIndexedProgMemLoad(LD, VT)) { + // It is legal to fold the load into an indexed load. + ResNode = CurDAG->getMachineNode(LPMOpc, DL, VT, MVT::i16, MVT::Other, Ptr, + RegZ); + ReplaceUses(SDValue(N, 1), SDValue(ResNode, 1)); + } else { + // Selecting an indexed load is not legal, fallback to a normal load. + switch (VT.SimpleTy) { + case MVT::i8: + ResNode = CurDAG->getMachineNode(AVR::LPMRdZ, DL, MVT::i8, MVT::Other, + Ptr, RegZ); + break; + case MVT::i16: + ResNode = CurDAG->getMachineNode(AVR::LPMWRdZ, DL, MVT::i16, + MVT::Other, Ptr, RegZ); + ReplaceUses(SDValue(N, 1), SDValue(ResNode, 1)); + break; + default: + llvm_unreachable("Unsupported VT!"); + } + } + + // Transfer memory operands. + MachineSDNode::mmo_iterator MemOp = MF->allocateMemRefsArray(1); + MemOp[0] = LD->getMemOperand(); + cast<MachineSDNode>(ResNode)->setMemRefs(MemOp, MemOp + 1); + + ReplaceUses(SDValue(N, 0), SDValue(ResNode, 0)); + ReplaceUses(SDValue(N, 1), SDValue(ResNode, 1)); + CurDAG->RemoveDeadNode(N); + + return true; +} + +template <> bool AVRDAGToDAGISel::select<AVRISD::CALL>(SDNode *N) { + SDValue InFlag; + SDValue Chain = N->getOperand(0); + SDValue Callee = N->getOperand(1); + unsigned LastOpNum = N->getNumOperands() - 1; + + // Direct calls are autogenerated. + unsigned Op = Callee.getOpcode(); + if (Op == ISD::TargetGlobalAddress || Op == ISD::TargetExternalSymbol) { + return false; + } + + // Skip the incoming flag if present + if (N->getOperand(LastOpNum).getValueType() == MVT::Glue) { + --LastOpNum; + } + + SDLoc DL(N); + Chain = CurDAG->getCopyToReg(Chain, DL, AVR::R31R30, Callee, InFlag); + SmallVector<SDValue, 8> Ops; + Ops.push_back(CurDAG->getRegister(AVR::R31R30, MVT::i16)); + + // Map all operands into the new node. + for (unsigned i = 2, e = LastOpNum + 1; i != e; ++i) { + Ops.push_back(N->getOperand(i)); + } + + Ops.push_back(Chain); + Ops.push_back(Chain.getValue(1)); + + SDNode *ResNode = + CurDAG->getMachineNode(AVR::ICALL, DL, MVT::Other, MVT::Glue, Ops); + + ReplaceUses(SDValue(N, 0), SDValue(ResNode, 0)); + ReplaceUses(SDValue(N, 1), SDValue(ResNode, 1)); + CurDAG->RemoveDeadNode(N); + + return true; +} + +template <> bool AVRDAGToDAGISel::select<ISD::BRIND>(SDNode *N) { + SDValue Chain = N->getOperand(0); + SDValue JmpAddr = N->getOperand(1); + + SDLoc DL(N); + // Move the destination address of the indirect branch into R31R30. + Chain = CurDAG->getCopyToReg(Chain, DL, AVR::R31R30, JmpAddr); + SDNode *ResNode = CurDAG->getMachineNode(AVR::IJMP, DL, MVT::Other, Chain); + + ReplaceUses(SDValue(N, 0), SDValue(ResNode, 0)); + CurDAG->RemoveDeadNode(N); + + return true; +} + +bool AVRDAGToDAGISel::selectMultiplication(llvm::SDNode *N) { + SDLoc DL(N); + MVT Type = N->getSimpleValueType(0); + + assert(Type == MVT::i8 && "unexpected value type"); + + bool isSigned = N->getOpcode() == ISD::SMUL_LOHI; + unsigned MachineOp = isSigned ? AVR::MULSRdRr : AVR::MULRdRr; + + SDValue Lhs = N->getOperand(0); + SDValue Rhs = N->getOperand(1); + SDNode *Mul = CurDAG->getMachineNode(MachineOp, DL, MVT::Glue, Lhs, Rhs); + SDValue InChain = CurDAG->getEntryNode(); + SDValue InGlue = SDValue(Mul, 0); + + // Copy the low half of the result, if it is needed. + if (N->hasAnyUseOfValue(0)) { + SDValue CopyFromLo = + CurDAG->getCopyFromReg(InChain, DL, AVR::R0, Type, InGlue); + + ReplaceUses(SDValue(N, 0), CopyFromLo); + + InChain = CopyFromLo.getValue(1); + InGlue = CopyFromLo.getValue(2); + } + + // Copy the high half of the result, if it is needed. + if (N->hasAnyUseOfValue(1)) { + SDValue CopyFromHi = + CurDAG->getCopyFromReg(InChain, DL, AVR::R1, Type, InGlue); + + ReplaceUses(SDValue(N, 1), CopyFromHi); + + InChain = CopyFromHi.getValue(1); + InGlue = CopyFromHi.getValue(2); + } + + CurDAG->RemoveDeadNode(N); + + // We need to clear R1. This is currently done (dirtily) + // using a custom inserter. + + return true; +} + +void AVRDAGToDAGISel::Select(SDNode *N) { + // Dump information about the Node being selected + DEBUG(errs() << "Selecting: "; N->dump(CurDAG); errs() << "\n"); + + // If we have a custom node, we already have selected! + if (N->isMachineOpcode()) { + DEBUG(errs() << "== "; N->dump(CurDAG); errs() << "\n"); + N->setNodeId(-1); + return; + } + + // See if subclasses can handle this node. + if (trySelect(N)) + return; + + // Select the default instruction + SelectCode(N); +} + +bool AVRDAGToDAGISel::trySelect(SDNode *N) { + unsigned Opcode = N->getOpcode(); + SDLoc DL(N); + + switch (Opcode) { + // Nodes we fully handle. + case ISD::FrameIndex: return select<ISD::FrameIndex>(N); + case ISD::BRIND: return select<ISD::BRIND>(N); + case ISD::UMUL_LOHI: + case ISD::SMUL_LOHI: return selectMultiplication(N); + + // Nodes we handle partially. Other cases are autogenerated + case ISD::STORE: return select<ISD::STORE>(N); + case ISD::LOAD: return select<ISD::LOAD>(N); + case AVRISD::CALL: return select<AVRISD::CALL>(N); + default: return false; + } +} + +FunctionPass *createAVRISelDag(AVRTargetMachine &TM, + CodeGenOpt::Level OptLevel) { + return new AVRDAGToDAGISel(TM, OptLevel); +} + +} // end of namespace llvm + diff --git a/contrib/llvm/lib/Target/AVR/AVRISelLowering.cpp b/contrib/llvm/lib/Target/AVR/AVRISelLowering.cpp new file mode 100644 index 0000000..07fc3f6 --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/AVRISelLowering.cpp @@ -0,0 +1,1978 @@ +//===-- AVRISelLowering.cpp - AVR DAG Lowering Implementation -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interfaces that AVR uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#include "AVRISelLowering.h" + +#include "llvm/ADT/StringSwitch.h" +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/SelectionDAG.h" +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" +#include "llvm/IR/Function.h" +#include "llvm/Support/ErrorHandling.h" + +#include "AVR.h" +#include "AVRMachineFunctionInfo.h" +#include "AVRTargetMachine.h" +#include "MCTargetDesc/AVRMCTargetDesc.h" + +namespace llvm { + +AVRTargetLowering::AVRTargetLowering(AVRTargetMachine &tm) + : TargetLowering(tm) { + // Set up the register classes. + addRegisterClass(MVT::i8, &AVR::GPR8RegClass); + addRegisterClass(MVT::i16, &AVR::DREGSRegClass); + + // Compute derived properties from the register classes. + computeRegisterProperties(tm.getSubtargetImpl()->getRegisterInfo()); + + setBooleanContents(ZeroOrOneBooleanContent); + setBooleanVectorContents(ZeroOrOneBooleanContent); + setSchedulingPreference(Sched::RegPressure); + setStackPointerRegisterToSaveRestore(AVR::SP); + + setOperationAction(ISD::GlobalAddress, MVT::i16, Custom); + setOperationAction(ISD::BlockAddress, MVT::i16, Custom); + + setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i8, Expand); + setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i16, Expand); + + for (MVT VT : MVT::integer_valuetypes()) { + for (auto N : {ISD::EXTLOAD, ISD::SEXTLOAD, ISD::ZEXTLOAD}) { + setLoadExtAction(N, VT, MVT::i1, Promote); + setLoadExtAction(N, VT, MVT::i8, Expand); + } + } + + setTruncStoreAction(MVT::i16, MVT::i8, Expand); + + // sub (x, imm) gets canonicalized to add (x, -imm), so for illegal types + // revert into a sub since we don't have an add with immediate instruction. + setOperationAction(ISD::ADD, MVT::i32, Custom); + setOperationAction(ISD::ADD, MVT::i64, Custom); + + // our shift instructions are only able to shift 1 bit at a time, so handle + // this in a custom way. + setOperationAction(ISD::SRA, MVT::i8, Custom); + setOperationAction(ISD::SHL, MVT::i8, Custom); + setOperationAction(ISD::SRL, MVT::i8, Custom); + setOperationAction(ISD::SRA, MVT::i16, Custom); + setOperationAction(ISD::SHL, MVT::i16, Custom); + setOperationAction(ISD::SRL, MVT::i16, Custom); + setOperationAction(ISD::SHL_PARTS, MVT::i16, Expand); + setOperationAction(ISD::SRA_PARTS, MVT::i16, Expand); + setOperationAction(ISD::SRL_PARTS, MVT::i16, Expand); + + setOperationAction(ISD::BR_CC, MVT::i8, Custom); + setOperationAction(ISD::BR_CC, MVT::i16, Custom); + setOperationAction(ISD::BR_CC, MVT::i32, Custom); + setOperationAction(ISD::BR_CC, MVT::i64, Custom); + setOperationAction(ISD::BRCOND, MVT::Other, Expand); + + setOperationAction(ISD::SELECT_CC, MVT::i8, Custom); + setOperationAction(ISD::SELECT_CC, MVT::i16, Custom); + setOperationAction(ISD::SELECT_CC, MVT::i32, Expand); + setOperationAction(ISD::SELECT_CC, MVT::i64, Expand); + setOperationAction(ISD::SETCC, MVT::i8, Custom); + setOperationAction(ISD::SETCC, MVT::i16, Custom); + setOperationAction(ISD::SETCC, MVT::i32, Custom); + setOperationAction(ISD::SETCC, MVT::i64, Custom); + setOperationAction(ISD::SELECT, MVT::i8, Expand); + setOperationAction(ISD::SELECT, MVT::i16, Expand); + + setOperationAction(ISD::BSWAP, MVT::i16, Expand); + + // Add support for postincrement and predecrement load/stores. + setIndexedLoadAction(ISD::POST_INC, MVT::i8, Legal); + setIndexedLoadAction(ISD::POST_INC, MVT::i16, Legal); + setIndexedLoadAction(ISD::PRE_DEC, MVT::i8, Legal); + setIndexedLoadAction(ISD::PRE_DEC, MVT::i16, Legal); + setIndexedStoreAction(ISD::POST_INC, MVT::i8, Legal); + setIndexedStoreAction(ISD::POST_INC, MVT::i16, Legal); + setIndexedStoreAction(ISD::PRE_DEC, MVT::i8, Legal); + setIndexedStoreAction(ISD::PRE_DEC, MVT::i16, Legal); + + setOperationAction(ISD::BR_JT, MVT::Other, Expand); + + setOperationAction(ISD::VASTART, MVT::Other, Custom); + setOperationAction(ISD::VAEND, MVT::Other, Expand); + setOperationAction(ISD::VAARG, MVT::Other, Expand); + setOperationAction(ISD::VACOPY, MVT::Other, Expand); + + // Atomic operations which must be lowered to rtlib calls + for (MVT VT : MVT::integer_valuetypes()) { + setOperationAction(ISD::ATOMIC_SWAP, VT, Expand); + setOperationAction(ISD::ATOMIC_CMP_SWAP, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_NAND, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_MAX, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_MIN, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_UMAX, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_UMIN, VT, Expand); + } + + // Division/remainder + setOperationAction(ISD::UDIV, MVT::i8, Expand); + setOperationAction(ISD::UDIV, MVT::i16, Expand); + setOperationAction(ISD::UREM, MVT::i8, Expand); + setOperationAction(ISD::UREM, MVT::i16, Expand); + setOperationAction(ISD::SDIV, MVT::i8, Expand); + setOperationAction(ISD::SDIV, MVT::i16, Expand); + setOperationAction(ISD::SREM, MVT::i8, Expand); + setOperationAction(ISD::SREM, MVT::i16, Expand); + + // Make division and modulus custom + for (MVT VT : MVT::integer_valuetypes()) { + setOperationAction(ISD::UDIVREM, VT, Custom); + setOperationAction(ISD::SDIVREM, VT, Custom); + } + + // Do not use MUL. The AVR instructions are closer to SMUL_LOHI &co. + setOperationAction(ISD::MUL, MVT::i8, Expand); + setOperationAction(ISD::MUL, MVT::i16, Expand); + + // Expand 16 bit multiplications. + setOperationAction(ISD::SMUL_LOHI, MVT::i16, Expand); + setOperationAction(ISD::UMUL_LOHI, MVT::i16, Expand); + + for (MVT VT : MVT::integer_valuetypes()) { + setOperationAction(ISD::MULHS, VT, Expand); + setOperationAction(ISD::MULHU, VT, Expand); + } + + for (MVT VT : MVT::integer_valuetypes()) { + setOperationAction(ISD::CTPOP, VT, Expand); + setOperationAction(ISD::CTLZ, VT, Expand); + setOperationAction(ISD::CTTZ, VT, Expand); + } + + for (MVT VT : MVT::integer_valuetypes()) { + setOperationAction(ISD::SIGN_EXTEND_INREG, VT, Expand); + // TODO: The generated code is pretty poor. Investigate using the + // same "shift and subtract with carry" trick that we do for + // extending 8-bit to 16-bit. This may require infrastructure + // improvements in how we treat 16-bit "registers" to be feasible. + } + + // Division rtlib functions (not supported) + setLibcallName(RTLIB::SDIV_I8, nullptr); + setLibcallName(RTLIB::SDIV_I16, nullptr); + setLibcallName(RTLIB::SDIV_I32, nullptr); + setLibcallName(RTLIB::SDIV_I64, nullptr); + setLibcallName(RTLIB::SDIV_I128, nullptr); + setLibcallName(RTLIB::UDIV_I8, nullptr); + setLibcallName(RTLIB::UDIV_I16, nullptr); + setLibcallName(RTLIB::UDIV_I32, nullptr); + setLibcallName(RTLIB::UDIV_I64, nullptr); + setLibcallName(RTLIB::UDIV_I128, nullptr); + + // Modulus rtlib functions (not supported) + setLibcallName(RTLIB::SREM_I8, nullptr); + setLibcallName(RTLIB::SREM_I16, nullptr); + setLibcallName(RTLIB::SREM_I32, nullptr); + setLibcallName(RTLIB::SREM_I64, nullptr); + setLibcallName(RTLIB::SREM_I128, nullptr); + setLibcallName(RTLIB::UREM_I8, nullptr); + setLibcallName(RTLIB::UREM_I16, nullptr); + setLibcallName(RTLIB::UREM_I32, nullptr); + setLibcallName(RTLIB::UREM_I64, nullptr); + setLibcallName(RTLIB::UREM_I128, nullptr); + + // Division and modulus rtlib functions + setLibcallName(RTLIB::SDIVREM_I8, "__divmodqi4"); + setLibcallName(RTLIB::SDIVREM_I16, "__divmodhi4"); + setLibcallName(RTLIB::SDIVREM_I32, "__divmodsi4"); + setLibcallName(RTLIB::SDIVREM_I64, "__divmoddi4"); + setLibcallName(RTLIB::SDIVREM_I128, "__divmodti4"); + setLibcallName(RTLIB::UDIVREM_I8, "__udivmodqi4"); + setLibcallName(RTLIB::UDIVREM_I16, "__udivmodhi4"); + setLibcallName(RTLIB::UDIVREM_I32, "__udivmodsi4"); + setLibcallName(RTLIB::UDIVREM_I64, "__udivmoddi4"); + setLibcallName(RTLIB::UDIVREM_I128, "__udivmodti4"); + + // Several of the runtime library functions use a special calling conv + setLibcallCallingConv(RTLIB::SDIVREM_I8, CallingConv::AVR_BUILTIN); + setLibcallCallingConv(RTLIB::SDIVREM_I16, CallingConv::AVR_BUILTIN); + setLibcallCallingConv(RTLIB::UDIVREM_I8, CallingConv::AVR_BUILTIN); + setLibcallCallingConv(RTLIB::UDIVREM_I16, CallingConv::AVR_BUILTIN); + + // Trigonometric rtlib functions + setLibcallName(RTLIB::SIN_F32, "sin"); + setLibcallName(RTLIB::COS_F32, "cos"); + + setMinFunctionAlignment(1); + setMinimumJumpTableEntries(INT_MAX); +} + +const char *AVRTargetLowering::getTargetNodeName(unsigned Opcode) const { +#define NODE(name) \ + case AVRISD::name: \ + return #name + + switch (Opcode) { + default: + return nullptr; + NODE(RET_FLAG); + NODE(RETI_FLAG); + NODE(CALL); + NODE(WRAPPER); + NODE(LSL); + NODE(LSR); + NODE(ROL); + NODE(ROR); + NODE(ASR); + NODE(LSLLOOP); + NODE(LSRLOOP); + NODE(ASRLOOP); + NODE(BRCOND); + NODE(CMP); + NODE(CMPC); + NODE(TST); + NODE(SELECT_CC); +#undef NODE + } +} + +EVT AVRTargetLowering::getSetCCResultType(const DataLayout &DL, LLVMContext &, + EVT VT) const { + assert(!VT.isVector() && "No AVR SetCC type for vectors!"); + return MVT::i8; +} + +SDValue AVRTargetLowering::LowerShifts(SDValue Op, SelectionDAG &DAG) const { + //:TODO: this function has to be completely rewritten to produce optimal + // code, for now it's producing very long but correct code. + unsigned Opc8; + const SDNode *N = Op.getNode(); + EVT VT = Op.getValueType(); + SDLoc dl(N); + + // Expand non-constant shifts to loops. + if (!isa<ConstantSDNode>(N->getOperand(1))) { + switch (Op.getOpcode()) { + default: + llvm_unreachable("Invalid shift opcode!"); + case ISD::SHL: + return DAG.getNode(AVRISD::LSLLOOP, dl, VT, N->getOperand(0), + N->getOperand(1)); + case ISD::SRL: + return DAG.getNode(AVRISD::LSRLOOP, dl, VT, N->getOperand(0), + N->getOperand(1)); + case ISD::SRA: + return DAG.getNode(AVRISD::ASRLOOP, dl, VT, N->getOperand(0), + N->getOperand(1)); + } + } + + uint64_t ShiftAmount = cast<ConstantSDNode>(N->getOperand(1))->getZExtValue(); + SDValue Victim = N->getOperand(0); + + switch (Op.getOpcode()) { + case ISD::SRA: + Opc8 = AVRISD::ASR; + break; + case ISD::ROTL: + Opc8 = AVRISD::ROL; + break; + case ISD::ROTR: + Opc8 = AVRISD::ROR; + break; + case ISD::SRL: + Opc8 = AVRISD::LSR; + break; + case ISD::SHL: + Opc8 = AVRISD::LSL; + break; + default: + llvm_unreachable("Invalid shift opcode"); + } + + while (ShiftAmount--) { + Victim = DAG.getNode(Opc8, dl, VT, Victim); + } + + return Victim; +} + +SDValue AVRTargetLowering::LowerDivRem(SDValue Op, SelectionDAG &DAG) const { + unsigned Opcode = Op->getOpcode(); + assert((Opcode == ISD::SDIVREM || Opcode == ISD::UDIVREM) && + "Invalid opcode for Div/Rem lowering"); + bool isSigned = (Opcode == ISD::SDIVREM); + EVT VT = Op->getValueType(0); + Type *Ty = VT.getTypeForEVT(*DAG.getContext()); + + RTLIB::Libcall LC; + switch (VT.getSimpleVT().SimpleTy) { + default: + llvm_unreachable("Unexpected request for libcall!"); + case MVT::i8: + LC = isSigned ? RTLIB::SDIVREM_I8 : RTLIB::UDIVREM_I8; + break; + case MVT::i16: + LC = isSigned ? RTLIB::SDIVREM_I16 : RTLIB::UDIVREM_I16; + break; + case MVT::i32: + LC = isSigned ? RTLIB::SDIVREM_I32 : RTLIB::UDIVREM_I32; + break; + case MVT::i64: + LC = isSigned ? RTLIB::SDIVREM_I64 : RTLIB::UDIVREM_I64; + break; + } + + SDValue InChain = DAG.getEntryNode(); + + TargetLowering::ArgListTy Args; + TargetLowering::ArgListEntry Entry; + for (SDValue const &Value : Op->op_values()) { + Entry.Node = Value; + Entry.Ty = Value.getValueType().getTypeForEVT(*DAG.getContext()); + Entry.isSExt = isSigned; + Entry.isZExt = !isSigned; + Args.push_back(Entry); + } + + SDValue Callee = DAG.getExternalSymbol(getLibcallName(LC), + getPointerTy(DAG.getDataLayout())); + + Type *RetTy = (Type *)StructType::get(Ty, Ty, nullptr); + + SDLoc dl(Op); + TargetLowering::CallLoweringInfo CLI(DAG); + CLI.setDebugLoc(dl) + .setChain(InChain) + .setCallee(getLibcallCallingConv(LC), RetTy, Callee, std::move(Args)) + .setInRegister() + .setSExtResult(isSigned) + .setZExtResult(!isSigned); + + std::pair<SDValue, SDValue> CallInfo = LowerCallTo(CLI); + return CallInfo.first; +} + +SDValue AVRTargetLowering::LowerGlobalAddress(SDValue Op, + SelectionDAG &DAG) const { + auto DL = DAG.getDataLayout(); + + const GlobalValue *GV = cast<GlobalAddressSDNode>(Op)->getGlobal(); + int64_t Offset = cast<GlobalAddressSDNode>(Op)->getOffset(); + + // Create the TargetGlobalAddress node, folding in the constant offset. + SDValue Result = + DAG.getTargetGlobalAddress(GV, SDLoc(Op), getPointerTy(DL), Offset); + return DAG.getNode(AVRISD::WRAPPER, SDLoc(Op), getPointerTy(DL), Result); +} + +SDValue AVRTargetLowering::LowerBlockAddress(SDValue Op, + SelectionDAG &DAG) const { + auto DL = DAG.getDataLayout(); + const BlockAddress *BA = cast<BlockAddressSDNode>(Op)->getBlockAddress(); + + SDValue Result = DAG.getTargetBlockAddress(BA, getPointerTy(DL)); + + return DAG.getNode(AVRISD::WRAPPER, SDLoc(Op), getPointerTy(DL), Result); +} + +/// IntCCToAVRCC - Convert a DAG integer condition code to an AVR CC. +static AVRCC::CondCodes intCCToAVRCC(ISD::CondCode CC) { + switch (CC) { + default: + llvm_unreachable("Unknown condition code!"); + case ISD::SETEQ: + return AVRCC::COND_EQ; + case ISD::SETNE: + return AVRCC::COND_NE; + case ISD::SETGE: + return AVRCC::COND_GE; + case ISD::SETLT: + return AVRCC::COND_LT; + case ISD::SETUGE: + return AVRCC::COND_SH; + case ISD::SETULT: + return AVRCC::COND_LO; + } +} + +/// Returns appropriate AVR CMP/CMPC nodes and corresponding condition code for +/// the given operands. +SDValue AVRTargetLowering::getAVRCmp(SDValue LHS, SDValue RHS, ISD::CondCode CC, + SDValue &AVRcc, SelectionDAG &DAG, + SDLoc DL) const { + SDValue Cmp; + EVT VT = LHS.getValueType(); + bool UseTest = false; + + switch (CC) { + default: + break; + case ISD::SETLE: { + // Swap operands and reverse the branching condition. + std::swap(LHS, RHS); + CC = ISD::SETGE; + break; + } + case ISD::SETGT: { + if (const ConstantSDNode *C = dyn_cast<ConstantSDNode>(RHS)) { + switch (C->getSExtValue()) { + case -1: { + // When doing lhs > -1 use a tst instruction on the top part of lhs + // and use brpl instead of using a chain of cp/cpc. + UseTest = true; + AVRcc = DAG.getConstant(AVRCC::COND_PL, DL, MVT::i8); + break; + } + case 0: { + // Turn lhs > 0 into 0 < lhs since 0 can be materialized with + // __zero_reg__ in lhs. + RHS = LHS; + LHS = DAG.getConstant(0, DL, VT); + CC = ISD::SETLT; + break; + } + default: { + // Turn lhs < rhs with lhs constant into rhs >= lhs+1, this allows + // us to fold the constant into the cmp instruction. + RHS = DAG.getConstant(C->getSExtValue() + 1, DL, VT); + CC = ISD::SETGE; + break; + } + } + break; + } + // Swap operands and reverse the branching condition. + std::swap(LHS, RHS); + CC = ISD::SETLT; + break; + } + case ISD::SETLT: { + if (const ConstantSDNode *C = dyn_cast<ConstantSDNode>(RHS)) { + switch (C->getSExtValue()) { + case 1: { + // Turn lhs < 1 into 0 >= lhs since 0 can be materialized with + // __zero_reg__ in lhs. + RHS = LHS; + LHS = DAG.getConstant(0, DL, VT); + CC = ISD::SETGE; + break; + } + case 0: { + // When doing lhs < 0 use a tst instruction on the top part of lhs + // and use brmi instead of using a chain of cp/cpc. + UseTest = true; + AVRcc = DAG.getConstant(AVRCC::COND_MI, DL, MVT::i8); + break; + } + } + } + break; + } + case ISD::SETULE: { + // Swap operands and reverse the branching condition. + std::swap(LHS, RHS); + CC = ISD::SETUGE; + break; + } + case ISD::SETUGT: { + // Turn lhs < rhs with lhs constant into rhs >= lhs+1, this allows us to + // fold the constant into the cmp instruction. + if (const ConstantSDNode *C = dyn_cast<ConstantSDNode>(RHS)) { + RHS = DAG.getConstant(C->getSExtValue() + 1, DL, VT); + CC = ISD::SETUGE; + break; + } + // Swap operands and reverse the branching condition. + std::swap(LHS, RHS); + CC = ISD::SETULT; + break; + } + } + + // Expand 32 and 64 bit comparisons with custom CMP and CMPC nodes instead of + // using the default and/or/xor expansion code which is much longer. + if (VT == MVT::i32) { + SDValue LHSlo = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i16, LHS, + DAG.getIntPtrConstant(0, DL)); + SDValue LHShi = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i16, LHS, + DAG.getIntPtrConstant(1, DL)); + SDValue RHSlo = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i16, RHS, + DAG.getIntPtrConstant(0, DL)); + SDValue RHShi = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i16, RHS, + DAG.getIntPtrConstant(1, DL)); + + if (UseTest) { + // When using tst we only care about the highest part. + SDValue Top = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i8, LHShi, + DAG.getIntPtrConstant(1, DL)); + Cmp = DAG.getNode(AVRISD::TST, DL, MVT::Glue, Top); + } else { + Cmp = DAG.getNode(AVRISD::CMP, DL, MVT::Glue, LHSlo, RHSlo); + Cmp = DAG.getNode(AVRISD::CMPC, DL, MVT::Glue, LHShi, RHShi, Cmp); + } + } else if (VT == MVT::i64) { + SDValue LHS_0 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i32, LHS, + DAG.getIntPtrConstant(0, DL)); + SDValue LHS_1 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i32, LHS, + DAG.getIntPtrConstant(1, DL)); + + SDValue LHS0 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i16, LHS_0, + DAG.getIntPtrConstant(0, DL)); + SDValue LHS1 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i16, LHS_0, + DAG.getIntPtrConstant(1, DL)); + SDValue LHS2 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i16, LHS_1, + DAG.getIntPtrConstant(0, DL)); + SDValue LHS3 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i16, LHS_1, + DAG.getIntPtrConstant(1, DL)); + + SDValue RHS_0 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i32, RHS, + DAG.getIntPtrConstant(0, DL)); + SDValue RHS_1 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i32, RHS, + DAG.getIntPtrConstant(1, DL)); + + SDValue RHS0 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i16, RHS_0, + DAG.getIntPtrConstant(0, DL)); + SDValue RHS1 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i16, RHS_0, + DAG.getIntPtrConstant(1, DL)); + SDValue RHS2 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i16, RHS_1, + DAG.getIntPtrConstant(0, DL)); + SDValue RHS3 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i16, RHS_1, + DAG.getIntPtrConstant(1, DL)); + + if (UseTest) { + // When using tst we only care about the highest part. + SDValue Top = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i8, LHS3, + DAG.getIntPtrConstant(1, DL)); + Cmp = DAG.getNode(AVRISD::TST, DL, MVT::Glue, Top); + } else { + Cmp = DAG.getNode(AVRISD::CMP, DL, MVT::Glue, LHS0, RHS0); + Cmp = DAG.getNode(AVRISD::CMPC, DL, MVT::Glue, LHS1, RHS1, Cmp); + Cmp = DAG.getNode(AVRISD::CMPC, DL, MVT::Glue, LHS2, RHS2, Cmp); + Cmp = DAG.getNode(AVRISD::CMPC, DL, MVT::Glue, LHS3, RHS3, Cmp); + } + } else if (VT == MVT::i8 || VT == MVT::i16) { + if (UseTest) { + // When using tst we only care about the highest part. + Cmp = DAG.getNode(AVRISD::TST, DL, MVT::Glue, + (VT == MVT::i8) + ? LHS + : DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i8, + LHS, DAG.getIntPtrConstant(1, DL))); + } else { + Cmp = DAG.getNode(AVRISD::CMP, DL, MVT::Glue, LHS, RHS); + } + } else { + llvm_unreachable("Invalid comparison size"); + } + + // When using a test instruction AVRcc is already set. + if (!UseTest) { + AVRcc = DAG.getConstant(intCCToAVRCC(CC), DL, MVT::i8); + } + + return Cmp; +} + +SDValue AVRTargetLowering::LowerBR_CC(SDValue Op, SelectionDAG &DAG) const { + SDValue Chain = Op.getOperand(0); + ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(1))->get(); + SDValue LHS = Op.getOperand(2); + SDValue RHS = Op.getOperand(3); + SDValue Dest = Op.getOperand(4); + SDLoc dl(Op); + + SDValue TargetCC; + SDValue Cmp = getAVRCmp(LHS, RHS, CC, TargetCC, DAG, dl); + + return DAG.getNode(AVRISD::BRCOND, dl, MVT::Other, Chain, Dest, TargetCC, + Cmp); +} + +SDValue AVRTargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const { + SDValue LHS = Op.getOperand(0); + SDValue RHS = Op.getOperand(1); + SDValue TrueV = Op.getOperand(2); + SDValue FalseV = Op.getOperand(3); + ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(4))->get(); + SDLoc dl(Op); + + SDValue TargetCC; + SDValue Cmp = getAVRCmp(LHS, RHS, CC, TargetCC, DAG, dl); + + SDVTList VTs = DAG.getVTList(Op.getValueType(), MVT::Glue); + SDValue Ops[] = {TrueV, FalseV, TargetCC, Cmp}; + + return DAG.getNode(AVRISD::SELECT_CC, dl, VTs, Ops); +} + +SDValue AVRTargetLowering::LowerSETCC(SDValue Op, SelectionDAG &DAG) const { + SDValue LHS = Op.getOperand(0); + SDValue RHS = Op.getOperand(1); + ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(2))->get(); + SDLoc DL(Op); + + SDValue TargetCC; + SDValue Cmp = getAVRCmp(LHS, RHS, CC, TargetCC, DAG, DL); + + SDValue TrueV = DAG.getConstant(1, DL, Op.getValueType()); + SDValue FalseV = DAG.getConstant(0, DL, Op.getValueType()); + SDVTList VTs = DAG.getVTList(Op.getValueType(), MVT::Glue); + SDValue Ops[] = {TrueV, FalseV, TargetCC, Cmp}; + + return DAG.getNode(AVRISD::SELECT_CC, DL, VTs, Ops); +} + +SDValue AVRTargetLowering::LowerVASTART(SDValue Op, SelectionDAG &DAG) const { + const MachineFunction &MF = DAG.getMachineFunction(); + const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>(); + const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue(); + auto DL = DAG.getDataLayout(); + SDLoc dl(Op); + + // Vastart just stores the address of the VarArgsFrameIndex slot into the + // memory location argument. + SDValue FI = DAG.getFrameIndex(AFI->getVarArgsFrameIndex(), getPointerTy(DL)); + + return DAG.getStore(Op.getOperand(0), dl, FI, Op.getOperand(1), + MachinePointerInfo(SV), 0); +} + +SDValue AVRTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const { + switch (Op.getOpcode()) { + default: + llvm_unreachable("Don't know how to custom lower this!"); + case ISD::SHL: + case ISD::SRA: + case ISD::SRL: + case ISD::ROTL: + case ISD::ROTR: + return LowerShifts(Op, DAG); + case ISD::GlobalAddress: + return LowerGlobalAddress(Op, DAG); + case ISD::BlockAddress: + return LowerBlockAddress(Op, DAG); + case ISD::BR_CC: + return LowerBR_CC(Op, DAG); + case ISD::SELECT_CC: + return LowerSELECT_CC(Op, DAG); + case ISD::SETCC: + return LowerSETCC(Op, DAG); + case ISD::VASTART: + return LowerVASTART(Op, DAG); + case ISD::SDIVREM: + case ISD::UDIVREM: + return LowerDivRem(Op, DAG); + } + + return SDValue(); +} + +/// Replace a node with an illegal result type +/// with a new node built out of custom code. +void AVRTargetLowering::ReplaceNodeResults(SDNode *N, + SmallVectorImpl<SDValue> &Results, + SelectionDAG &DAG) const { + SDLoc DL(N); + + switch (N->getOpcode()) { + case ISD::ADD: { + // Convert add (x, imm) into sub (x, -imm). + if (const ConstantSDNode *C = dyn_cast<ConstantSDNode>(N->getOperand(1))) { + SDValue Sub = DAG.getNode( + ISD::SUB, DL, N->getValueType(0), N->getOperand(0), + DAG.getConstant(-C->getAPIntValue(), DL, C->getValueType(0))); + Results.push_back(Sub); + } + break; + } + default: { + SDValue Res = LowerOperation(SDValue(N, 0), DAG); + + for (unsigned I = 0, E = Res->getNumValues(); I != E; ++I) + Results.push_back(Res.getValue(I)); + + break; + } + } +} + +/// Return true if the addressing mode represented +/// by AM is legal for this target, for a load/store of the specified type. +bool AVRTargetLowering::isLegalAddressingMode(const DataLayout &DL, + const AddrMode &AM, Type *Ty, + unsigned AS) const { + int64_t Offs = AM.BaseOffs; + + // Allow absolute addresses. + if (AM.BaseGV && !AM.HasBaseReg && AM.Scale == 0 && Offs == 0) { + return true; + } + + // Flash memory instructions only allow zero offsets. + if (isa<PointerType>(Ty) && AS == AVR::ProgramMemory) { + return false; + } + + // Allow reg+<6bit> offset. + if (Offs < 0) + Offs = -Offs; + if (AM.BaseGV == 0 && AM.HasBaseReg && AM.Scale == 0 && isUInt<6>(Offs)) { + return true; + } + + return false; +} + +/// Returns true by value, base pointer and +/// offset pointer and addressing mode by reference if the node's address +/// can be legally represented as pre-indexed load / store address. +bool AVRTargetLowering::getPreIndexedAddressParts(SDNode *N, SDValue &Base, + SDValue &Offset, + ISD::MemIndexedMode &AM, + SelectionDAG &DAG) const { + EVT VT; + const SDNode *Op; + SDLoc DL(N); + + if (const LoadSDNode *LD = dyn_cast<LoadSDNode>(N)) { + VT = LD->getMemoryVT(); + Op = LD->getBasePtr().getNode(); + if (LD->getExtensionType() != ISD::NON_EXTLOAD) + return false; + if (AVR::isProgramMemoryAccess(LD)) { + return false; + } + } else if (const StoreSDNode *ST = dyn_cast<StoreSDNode>(N)) { + VT = ST->getMemoryVT(); + Op = ST->getBasePtr().getNode(); + if (AVR::isProgramMemoryAccess(ST)) { + return false; + } + } else { + return false; + } + + if (VT != MVT::i8 && VT != MVT::i16) { + return false; + } + + if (Op->getOpcode() != ISD::ADD && Op->getOpcode() != ISD::SUB) { + return false; + } + + if (const ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(Op->getOperand(1))) { + int RHSC = RHS->getSExtValue(); + if (Op->getOpcode() == ISD::SUB) + RHSC = -RHSC; + + if ((VT == MVT::i16 && RHSC != -2) || (VT == MVT::i8 && RHSC != -1)) { + return false; + } + + Base = Op->getOperand(0); + Offset = DAG.getConstant(RHSC, DL, MVT::i8); + AM = ISD::PRE_DEC; + + return true; + } + + return false; +} + +/// Returns true by value, base pointer and +/// offset pointer and addressing mode by reference if this node can be +/// combined with a load / store to form a post-indexed load / store. +bool AVRTargetLowering::getPostIndexedAddressParts(SDNode *N, SDNode *Op, + SDValue &Base, + SDValue &Offset, + ISD::MemIndexedMode &AM, + SelectionDAG &DAG) const { + EVT VT; + SDLoc DL(N); + + if (const LoadSDNode *LD = dyn_cast<LoadSDNode>(N)) { + VT = LD->getMemoryVT(); + if (LD->getExtensionType() != ISD::NON_EXTLOAD) + return false; + } else if (const StoreSDNode *ST = dyn_cast<StoreSDNode>(N)) { + VT = ST->getMemoryVT(); + if (AVR::isProgramMemoryAccess(ST)) { + return false; + } + } else { + return false; + } + + if (VT != MVT::i8 && VT != MVT::i16) { + return false; + } + + if (Op->getOpcode() != ISD::ADD && Op->getOpcode() != ISD::SUB) { + return false; + } + + if (const ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(Op->getOperand(1))) { + int RHSC = RHS->getSExtValue(); + if (Op->getOpcode() == ISD::SUB) + RHSC = -RHSC; + if ((VT == MVT::i16 && RHSC != 2) || (VT == MVT::i8 && RHSC != 1)) { + return false; + } + + Base = Op->getOperand(0); + Offset = DAG.getConstant(RHSC, DL, MVT::i8); + AM = ISD::POST_INC; + + return true; + } + + return false; +} + +bool AVRTargetLowering::isOffsetFoldingLegal( + const GlobalAddressSDNode *GA) const { + return true; +} + +//===----------------------------------------------------------------------===// +// Formal Arguments Calling Convention Implementation +//===----------------------------------------------------------------------===// + +#include "AVRGenCallingConv.inc" + +/// For each argument in a function store the number of pieces it is composed +/// of. +static void parseFunctionArgs(const Function *F, const DataLayout *TD, + SmallVectorImpl<unsigned> &Out) { + for (Argument const &Arg : F->args()) { + unsigned Bytes = (TD->getTypeSizeInBits(Arg.getType()) + 7) / 8; + Out.push_back((Bytes + 1) / 2); + } +} + +/// For external symbols there is no function prototype information so we +/// have to rely directly on argument sizes. +static void parseExternFuncCallArgs(const SmallVectorImpl<ISD::OutputArg> &In, + SmallVectorImpl<unsigned> &Out) { + for (unsigned i = 0, e = In.size(); i != e;) { + unsigned Size = 0; + unsigned Offset = 0; + while ((i != e) && (In[i].PartOffset == Offset)) { + Offset += In[i].VT.getStoreSize(); + ++i; + ++Size; + } + Out.push_back(Size); + } +} + +static StringRef getFunctionName(TargetLowering::CallLoweringInfo &CLI) { + SDValue Callee = CLI.Callee; + + if (const ExternalSymbolSDNode *G = dyn_cast<ExternalSymbolSDNode>(Callee)) { + return G->getSymbol(); + } + + if (const GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) { + return G->getGlobal()->getName(); + } + + llvm_unreachable("don't know how to get the name for this callee"); +} + +/// Analyze incoming and outgoing function arguments. We need custom C++ code +/// to handle special constraints in the ABI like reversing the order of the +/// pieces of splitted arguments. In addition, all pieces of a certain argument +/// have to be passed either using registers or the stack but never mixing both. +static void analyzeStandardArguments(TargetLowering::CallLoweringInfo *CLI, + const Function *F, const DataLayout *TD, + const SmallVectorImpl<ISD::OutputArg> *Outs, + const SmallVectorImpl<ISD::InputArg> *Ins, + CallingConv::ID CallConv, + SmallVectorImpl<CCValAssign> &ArgLocs, + CCState &CCInfo, bool IsCall, bool IsVarArg) { + static const MCPhysReg RegList8[] = {AVR::R24, AVR::R22, AVR::R20, + AVR::R18, AVR::R16, AVR::R14, + AVR::R12, AVR::R10, AVR::R8}; + static const MCPhysReg RegList16[] = {AVR::R25R24, AVR::R23R22, AVR::R21R20, + AVR::R19R18, AVR::R17R16, AVR::R15R14, + AVR::R13R12, AVR::R11R10, AVR::R9R8}; + if (IsVarArg) { + // Variadic functions do not need all the analisys below. + if (IsCall) { + CCInfo.AnalyzeCallOperands(*Outs, ArgCC_AVR_Vararg); + } else { + CCInfo.AnalyzeFormalArguments(*Ins, ArgCC_AVR_Vararg); + } + return; + } + + // Fill in the Args array which will contain original argument sizes. + SmallVector<unsigned, 8> Args; + if (IsCall) { + parseExternFuncCallArgs(*Outs, Args); + } else { + assert(F != nullptr && "function should not be null"); + parseFunctionArgs(F, TD, Args); + } + + unsigned RegsLeft = array_lengthof(RegList8), ValNo = 0; + // Variadic functions always use the stack. + bool UsesStack = false; + for (unsigned i = 0, pos = 0, e = Args.size(); i != e; ++i) { + unsigned Size = Args[i]; + MVT LocVT = (IsCall) ? (*Outs)[pos].VT : (*Ins)[pos].VT; + + // If we have plenty of regs to pass the whole argument do it. + if (!UsesStack && (Size <= RegsLeft)) { + const MCPhysReg *RegList = (LocVT == MVT::i16) ? RegList16 : RegList8; + + for (unsigned j = 0; j != Size; ++j) { + unsigned Reg = CCInfo.AllocateReg( + ArrayRef<MCPhysReg>(RegList, array_lengthof(RegList8))); + CCInfo.addLoc( + CCValAssign::getReg(ValNo++, LocVT, Reg, LocVT, CCValAssign::Full)); + --RegsLeft; + } + + // Reverse the order of the pieces to agree with the "big endian" format + // required in the calling convention ABI. + std::reverse(ArgLocs.begin() + pos, ArgLocs.begin() + pos + Size); + } else { + // Pass the rest of arguments using the stack. + UsesStack = true; + for (unsigned j = 0; j != Size; ++j) { + unsigned Offset = CCInfo.AllocateStack( + TD->getTypeAllocSize(EVT(LocVT).getTypeForEVT(CCInfo.getContext())), + TD->getABITypeAlignment( + EVT(LocVT).getTypeForEVT(CCInfo.getContext()))); + CCInfo.addLoc(CCValAssign::getMem(ValNo++, LocVT, Offset, LocVT, + CCValAssign::Full)); + } + } + pos += Size; + } +} + +static void analyzeBuiltinArguments(TargetLowering::CallLoweringInfo &CLI, + const Function *F, const DataLayout *TD, + const SmallVectorImpl<ISD::OutputArg> *Outs, + const SmallVectorImpl<ISD::InputArg> *Ins, + CallingConv::ID CallConv, + SmallVectorImpl<CCValAssign> &ArgLocs, + CCState &CCInfo, bool IsCall, bool IsVarArg) { + StringRef FuncName = getFunctionName(CLI); + + if (FuncName.startswith("__udivmod") || FuncName.startswith("__divmod")) { + CCInfo.AnalyzeCallOperands(*Outs, ArgCC_AVR_BUILTIN_DIV); + } else { + analyzeStandardArguments(&CLI, F, TD, Outs, Ins, + CallConv, ArgLocs, CCInfo, + IsCall, IsVarArg); + } +} + +static void analyzeArguments(TargetLowering::CallLoweringInfo *CLI, + const Function *F, const DataLayout *TD, + const SmallVectorImpl<ISD::OutputArg> *Outs, + const SmallVectorImpl<ISD::InputArg> *Ins, + CallingConv::ID CallConv, + SmallVectorImpl<CCValAssign> &ArgLocs, + CCState &CCInfo, bool IsCall, bool IsVarArg) { + switch (CallConv) { + case CallingConv::AVR_BUILTIN: { + analyzeBuiltinArguments(*CLI, F, TD, Outs, Ins, + CallConv, ArgLocs, CCInfo, + IsCall, IsVarArg); + return; + } + default: { + analyzeStandardArguments(CLI, F, TD, Outs, Ins, + CallConv, ArgLocs, CCInfo, + IsCall, IsVarArg); + return; + } + } +} + +SDValue AVRTargetLowering::LowerFormalArguments( + SDValue Chain, CallingConv::ID CallConv, bool isVarArg, + const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &dl, SelectionDAG &DAG, + SmallVectorImpl<SDValue> &InVals) const { + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + auto DL = DAG.getDataLayout(); + + // Assign locations to all of the incoming arguments. + SmallVector<CCValAssign, 16> ArgLocs; + CCState CCInfo(CallConv, isVarArg, DAG.getMachineFunction(), ArgLocs, + *DAG.getContext()); + + analyzeArguments(nullptr, MF.getFunction(), &DL, 0, &Ins, CallConv, ArgLocs, CCInfo, + false, isVarArg); + + SDValue ArgValue; + for (CCValAssign &VA : ArgLocs) { + + // Arguments stored on registers. + if (VA.isRegLoc()) { + EVT RegVT = VA.getLocVT(); + const TargetRegisterClass *RC; + if (RegVT == MVT::i8) { + RC = &AVR::GPR8RegClass; + } else if (RegVT == MVT::i16) { + RC = &AVR::DREGSRegClass; + } else { + llvm_unreachable("Unknown argument type!"); + } + + unsigned Reg = MF.addLiveIn(VA.getLocReg(), RC); + ArgValue = DAG.getCopyFromReg(Chain, dl, Reg, RegVT); + + // :NOTE: Clang should not promote any i8 into i16 but for safety the + // following code will handle zexts or sexts generated by other + // front ends. Otherwise: + // If this is an 8 bit value, it is really passed promoted + // to 16 bits. Insert an assert[sz]ext to capture this, then + // truncate to the right size. + switch (VA.getLocInfo()) { + default: + llvm_unreachable("Unknown loc info!"); + case CCValAssign::Full: + break; + case CCValAssign::BCvt: + ArgValue = DAG.getNode(ISD::BITCAST, dl, VA.getValVT(), ArgValue); + break; + case CCValAssign::SExt: + ArgValue = DAG.getNode(ISD::AssertSext, dl, RegVT, ArgValue, + DAG.getValueType(VA.getValVT())); + ArgValue = DAG.getNode(ISD::TRUNCATE, dl, VA.getValVT(), ArgValue); + break; + case CCValAssign::ZExt: + ArgValue = DAG.getNode(ISD::AssertZext, dl, RegVT, ArgValue, + DAG.getValueType(VA.getValVT())); + ArgValue = DAG.getNode(ISD::TRUNCATE, dl, VA.getValVT(), ArgValue); + break; + } + + InVals.push_back(ArgValue); + } else { + // Sanity check. + assert(VA.isMemLoc()); + + EVT LocVT = VA.getLocVT(); + + // Create the frame index object for this incoming parameter. + int FI = MFI.CreateFixedObject(LocVT.getSizeInBits() / 8, + VA.getLocMemOffset(), true); + + // Create the SelectionDAG nodes corresponding to a load + // from this parameter. + SDValue FIN = DAG.getFrameIndex(FI, getPointerTy(DL)); + InVals.push_back(DAG.getLoad(LocVT, dl, Chain, FIN, + MachinePointerInfo::getFixedStack(MF, FI), + 0)); + } + } + + // If the function takes variable number of arguments, make a frame index for + // the start of the first vararg value... for expansion of llvm.va_start. + if (isVarArg) { + unsigned StackSize = CCInfo.getNextStackOffset(); + AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>(); + + AFI->setVarArgsFrameIndex(MFI.CreateFixedObject(2, StackSize, true)); + } + + return Chain; +} + +//===----------------------------------------------------------------------===// +// Call Calling Convention Implementation +//===----------------------------------------------------------------------===// + +SDValue AVRTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, + SmallVectorImpl<SDValue> &InVals) const { + SelectionDAG &DAG = CLI.DAG; + SDLoc &DL = CLI.DL; + SmallVectorImpl<ISD::OutputArg> &Outs = CLI.Outs; + SmallVectorImpl<SDValue> &OutVals = CLI.OutVals; + SmallVectorImpl<ISD::InputArg> &Ins = CLI.Ins; + SDValue Chain = CLI.Chain; + SDValue Callee = CLI.Callee; + bool &isTailCall = CLI.IsTailCall; + CallingConv::ID CallConv = CLI.CallConv; + bool isVarArg = CLI.IsVarArg; + + MachineFunction &MF = DAG.getMachineFunction(); + + // AVR does not yet support tail call optimization. + isTailCall = false; + + // Analyze operands of the call, assigning locations to each operand. + SmallVector<CCValAssign, 16> ArgLocs; + CCState CCInfo(CallConv, isVarArg, DAG.getMachineFunction(), ArgLocs, + *DAG.getContext()); + + // If the callee is a GlobalAddress/ExternalSymbol node (quite common, every + // direct call is) turn it into a TargetGlobalAddress/TargetExternalSymbol + // node so that legalize doesn't hack it. + const Function *F = nullptr; + if (const GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) { + const GlobalValue *GV = G->getGlobal(); + + F = cast<Function>(GV); + Callee = + DAG.getTargetGlobalAddress(GV, DL, getPointerTy(DAG.getDataLayout())); + } else if (const ExternalSymbolSDNode *ES = + dyn_cast<ExternalSymbolSDNode>(Callee)) { + Callee = DAG.getTargetExternalSymbol(ES->getSymbol(), + getPointerTy(DAG.getDataLayout())); + } + + analyzeArguments(&CLI, F, &DAG.getDataLayout(), &Outs, 0, CallConv, ArgLocs, CCInfo, + true, isVarArg); + + // Get a count of how many bytes are to be pushed on the stack. + unsigned NumBytes = CCInfo.getNextStackOffset(); + + Chain = DAG.getCALLSEQ_START(Chain, DAG.getIntPtrConstant(NumBytes, DL, true), + DL); + + SmallVector<std::pair<unsigned, SDValue>, 8> RegsToPass; + + // First, walk the register assignments, inserting copies. + unsigned AI, AE; + bool HasStackArgs = false; + for (AI = 0, AE = ArgLocs.size(); AI != AE; ++AI) { + CCValAssign &VA = ArgLocs[AI]; + EVT RegVT = VA.getLocVT(); + SDValue Arg = OutVals[AI]; + + // Promote the value if needed. With Clang this should not happen. + switch (VA.getLocInfo()) { + default: + llvm_unreachable("Unknown loc info!"); + case CCValAssign::Full: + break; + case CCValAssign::SExt: + Arg = DAG.getNode(ISD::SIGN_EXTEND, DL, RegVT, Arg); + break; + case CCValAssign::ZExt: + Arg = DAG.getNode(ISD::ZERO_EXTEND, DL, RegVT, Arg); + break; + case CCValAssign::AExt: + Arg = DAG.getNode(ISD::ANY_EXTEND, DL, RegVT, Arg); + break; + case CCValAssign::BCvt: + Arg = DAG.getNode(ISD::BITCAST, DL, RegVT, Arg); + break; + } + + // Stop when we encounter a stack argument, we need to process them + // in reverse order in the loop below. + if (VA.isMemLoc()) { + HasStackArgs = true; + break; + } + + // Arguments that can be passed on registers must be kept in the RegsToPass + // vector. + RegsToPass.push_back(std::make_pair(VA.getLocReg(), Arg)); + } + + // Second, stack arguments have to walked in reverse order by inserting + // chained stores, this ensures their order is not changed by the scheduler + // and that the push instruction sequence generated is correct, otherwise they + // can be freely intermixed. + if (HasStackArgs) { + for (AE = AI, AI = ArgLocs.size(); AI != AE; --AI) { + unsigned Loc = AI - 1; + CCValAssign &VA = ArgLocs[Loc]; + SDValue Arg = OutVals[Loc]; + + assert(VA.isMemLoc()); + + // SP points to one stack slot further so add one to adjust it. + SDValue PtrOff = DAG.getNode( + ISD::ADD, DL, getPointerTy(DAG.getDataLayout()), + DAG.getRegister(AVR::SP, getPointerTy(DAG.getDataLayout())), + DAG.getIntPtrConstant(VA.getLocMemOffset() + 1, DL)); + + Chain = + DAG.getStore(Chain, DL, Arg, PtrOff, + MachinePointerInfo::getStack(MF, VA.getLocMemOffset()), + 0); + } + } + + // Build a sequence of copy-to-reg nodes chained together with token chain and + // flag operands which copy the outgoing args into registers. The InFlag in + // necessary since all emited instructions must be stuck together. + SDValue InFlag; + for (auto Reg : RegsToPass) { + Chain = DAG.getCopyToReg(Chain, DL, Reg.first, Reg.second, InFlag); + InFlag = Chain.getValue(1); + } + + // Returns a chain & a flag for retval copy to use. + SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); + SmallVector<SDValue, 8> Ops; + Ops.push_back(Chain); + Ops.push_back(Callee); + + // Add argument registers to the end of the list so that they are known live + // into the call. + for (auto Reg : RegsToPass) { + Ops.push_back(DAG.getRegister(Reg.first, Reg.second.getValueType())); + } + + // Add a register mask operand representing the call-preserved registers. + const AVRTargetMachine &TM = (const AVRTargetMachine &)getTargetMachine(); + const TargetRegisterInfo *TRI = TM.getSubtargetImpl()->getRegisterInfo(); + const uint32_t *Mask = + TRI->getCallPreservedMask(DAG.getMachineFunction(), CallConv); + assert(Mask && "Missing call preserved mask for calling convention"); + Ops.push_back(DAG.getRegisterMask(Mask)); + + if (InFlag.getNode()) { + Ops.push_back(InFlag); + } + + Chain = DAG.getNode(AVRISD::CALL, DL, NodeTys, Ops); + InFlag = Chain.getValue(1); + + // Create the CALLSEQ_END node. + Chain = DAG.getCALLSEQ_END(Chain, DAG.getIntPtrConstant(NumBytes, DL, true), + DAG.getIntPtrConstant(0, DL, true), InFlag, DL); + + if (!Ins.empty()) { + InFlag = Chain.getValue(1); + } + + // Handle result values, copying them out of physregs into vregs that we + // return. + return LowerCallResult(Chain, InFlag, CallConv, isVarArg, Ins, DL, DAG, + InVals); +} + +/// Lower the result values of a call into the +/// appropriate copies out of appropriate physical registers. +/// +SDValue AVRTargetLowering::LowerCallResult( + SDValue Chain, SDValue InFlag, CallingConv::ID CallConv, bool isVarArg, + const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &dl, SelectionDAG &DAG, + SmallVectorImpl<SDValue> &InVals) const { + + // Assign locations to each value returned by this call. + SmallVector<CCValAssign, 16> RVLocs; + CCState CCInfo(CallConv, isVarArg, DAG.getMachineFunction(), RVLocs, + *DAG.getContext()); + + // Handle runtime calling convs. + auto CCFunction = CCAssignFnForReturn(CallConv); + CCInfo.AnalyzeCallResult(Ins, CCFunction); + + if (CallConv != CallingConv::AVR_BUILTIN && RVLocs.size() > 1) { + // Reverse splitted return values to get the "big endian" format required + // to agree with the calling convention ABI. + std::reverse(RVLocs.begin(), RVLocs.end()); + } + + // Copy all of the result registers out of their specified physreg. + for (CCValAssign const &RVLoc : RVLocs) { + Chain = DAG.getCopyFromReg(Chain, dl, RVLoc.getLocReg(), RVLoc.getValVT(), + InFlag) + .getValue(1); + InFlag = Chain.getValue(2); + InVals.push_back(Chain.getValue(0)); + } + + return Chain; +} + +//===----------------------------------------------------------------------===// +// Return Value Calling Convention Implementation +//===----------------------------------------------------------------------===// + +CCAssignFn *AVRTargetLowering::CCAssignFnForReturn(CallingConv::ID CC) const { + switch (CC) { + case CallingConv::AVR_BUILTIN: + return RetCC_AVR_BUILTIN; + default: + return RetCC_AVR; + } +} + +bool +AVRTargetLowering::CanLowerReturn(CallingConv::ID CallConv, + MachineFunction &MF, bool isVarArg, + const SmallVectorImpl<ISD::OutputArg> &Outs, + LLVMContext &Context) const +{ + SmallVector<CCValAssign, 16> RVLocs; + CCState CCInfo(CallConv, isVarArg, MF, RVLocs, Context); + + auto CCFunction = CCAssignFnForReturn(CallConv); + return CCInfo.CheckReturn(Outs, CCFunction); +} + +SDValue +AVRTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, + bool isVarArg, + const SmallVectorImpl<ISD::OutputArg> &Outs, + const SmallVectorImpl<SDValue> &OutVals, + const SDLoc &dl, SelectionDAG &DAG) const { + // CCValAssign - represent the assignment of the return value to locations. + SmallVector<CCValAssign, 16> RVLocs; + + // CCState - Info about the registers and stack slot. + CCState CCInfo(CallConv, isVarArg, DAG.getMachineFunction(), RVLocs, + *DAG.getContext()); + + // Analyze return values. + auto CCFunction = CCAssignFnForReturn(CallConv); + CCInfo.AnalyzeReturn(Outs, CCFunction); + + // If this is the first return lowered for this function, add the regs to + // the liveout set for the function. + MachineFunction &MF = DAG.getMachineFunction(); + unsigned e = RVLocs.size(); + + // Reverse splitted return values to get the "big endian" format required + // to agree with the calling convention ABI. + if (e > 1) { + std::reverse(RVLocs.begin(), RVLocs.end()); + } + + SDValue Flag; + SmallVector<SDValue, 4> RetOps(1, Chain); + // Copy the result values into the output registers. + for (unsigned i = 0; i != e; ++i) { + CCValAssign &VA = RVLocs[i]; + assert(VA.isRegLoc() && "Can only return in registers!"); + + Chain = DAG.getCopyToReg(Chain, dl, VA.getLocReg(), OutVals[i], Flag); + + // Guarantee that all emitted copies are stuck together with flags. + Flag = Chain.getValue(1); + RetOps.push_back(DAG.getRegister(VA.getLocReg(), VA.getLocVT())); + } + + // Don't emit the ret/reti instruction when the naked attribute is present in + // the function being compiled. + if (MF.getFunction()->getAttributes().hasAttribute( + AttributeSet::FunctionIndex, Attribute::Naked)) { + return Chain; + } + + unsigned RetOpc = + (CallConv == CallingConv::AVR_INTR || CallConv == CallingConv::AVR_SIGNAL) + ? AVRISD::RETI_FLAG + : AVRISD::RET_FLAG; + + RetOps[0] = Chain; // Update chain. + + if (Flag.getNode()) { + RetOps.push_back(Flag); + } + + return DAG.getNode(RetOpc, dl, MVT::Other, RetOps); +} + +//===----------------------------------------------------------------------===// +// Custom Inserters +//===----------------------------------------------------------------------===// + +MachineBasicBlock *AVRTargetLowering::insertShift(MachineInstr &MI, + MachineBasicBlock *BB) const { + unsigned Opc; + const TargetRegisterClass *RC; + MachineFunction *F = BB->getParent(); + MachineRegisterInfo &RI = F->getRegInfo(); + const AVRTargetMachine &TM = (const AVRTargetMachine &)getTargetMachine(); + const TargetInstrInfo &TII = *TM.getSubtargetImpl()->getInstrInfo(); + DebugLoc dl = MI.getDebugLoc(); + + switch (MI.getOpcode()) { + default: + llvm_unreachable("Invalid shift opcode!"); + case AVR::Lsl8: + Opc = AVR::LSLRd; + RC = &AVR::GPR8RegClass; + break; + case AVR::Lsl16: + Opc = AVR::LSLWRd; + RC = &AVR::DREGSRegClass; + break; + case AVR::Asr8: + Opc = AVR::ASRRd; + RC = &AVR::GPR8RegClass; + break; + case AVR::Asr16: + Opc = AVR::ASRWRd; + RC = &AVR::DREGSRegClass; + break; + case AVR::Lsr8: + Opc = AVR::LSRRd; + RC = &AVR::GPR8RegClass; + break; + case AVR::Lsr16: + Opc = AVR::LSRWRd; + RC = &AVR::DREGSRegClass; + break; + } + + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineFunction::iterator I = BB->getParent()->begin(); + ++I; + + // Create loop block. + MachineBasicBlock *LoopBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *RemBB = F->CreateMachineBasicBlock(LLVM_BB); + + F->insert(I, LoopBB); + F->insert(I, RemBB); + + // Update machine-CFG edges by transferring all successors of the current + // block to the block containing instructions after shift. + RemBB->splice(RemBB->begin(), BB, std::next(MachineBasicBlock::iterator(MI)), + BB->end()); + RemBB->transferSuccessorsAndUpdatePHIs(BB); + + // Add adges BB => LoopBB => RemBB, BB => RemBB, LoopBB => LoopBB. + BB->addSuccessor(LoopBB); + BB->addSuccessor(RemBB); + LoopBB->addSuccessor(RemBB); + LoopBB->addSuccessor(LoopBB); + + unsigned ShiftAmtReg = RI.createVirtualRegister(&AVR::LD8RegClass); + unsigned ShiftAmtReg2 = RI.createVirtualRegister(&AVR::LD8RegClass); + unsigned ShiftReg = RI.createVirtualRegister(RC); + unsigned ShiftReg2 = RI.createVirtualRegister(RC); + unsigned ShiftAmtSrcReg = MI.getOperand(2).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + unsigned DstReg = MI.getOperand(0).getReg(); + + // BB: + // cp 0, N + // breq RemBB + BuildMI(BB, dl, TII.get(AVR::CPRdRr)).addReg(ShiftAmtSrcReg).addReg(AVR::R0); + BuildMI(BB, dl, TII.get(AVR::BREQk)).addMBB(RemBB); + + // LoopBB: + // ShiftReg = phi [%SrcReg, BB], [%ShiftReg2, LoopBB] + // ShiftAmt = phi [%N, BB], [%ShiftAmt2, LoopBB] + // ShiftReg2 = shift ShiftReg + // ShiftAmt2 = ShiftAmt - 1; + BuildMI(LoopBB, dl, TII.get(AVR::PHI), ShiftReg) + .addReg(SrcReg) + .addMBB(BB) + .addReg(ShiftReg2) + .addMBB(LoopBB); + BuildMI(LoopBB, dl, TII.get(AVR::PHI), ShiftAmtReg) + .addReg(ShiftAmtSrcReg) + .addMBB(BB) + .addReg(ShiftAmtReg2) + .addMBB(LoopBB); + BuildMI(LoopBB, dl, TII.get(Opc), ShiftReg2).addReg(ShiftReg); + BuildMI(LoopBB, dl, TII.get(AVR::SUBIRdK), ShiftAmtReg2) + .addReg(ShiftAmtReg) + .addImm(1); + BuildMI(LoopBB, dl, TII.get(AVR::BRNEk)).addMBB(LoopBB); + + // RemBB: + // DestReg = phi [%SrcReg, BB], [%ShiftReg, LoopBB] + BuildMI(*RemBB, RemBB->begin(), dl, TII.get(AVR::PHI), DstReg) + .addReg(SrcReg) + .addMBB(BB) + .addReg(ShiftReg2) + .addMBB(LoopBB); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return RemBB; +} + +static bool isCopyMulResult(MachineBasicBlock::iterator const &I) { + if (I->getOpcode() == AVR::COPY) { + unsigned SrcReg = I->getOperand(1).getReg(); + return (SrcReg == AVR::R0 || SrcReg == AVR::R1); + } + + return false; +} + +// The mul instructions wreak havock on our zero_reg R1. We need to clear it +// after the result has been evacuated. This is probably not the best way to do +// it, but it works for now. +MachineBasicBlock *AVRTargetLowering::insertMul(MachineInstr &MI, + MachineBasicBlock *BB) const { + const AVRTargetMachine &TM = (const AVRTargetMachine &)getTargetMachine(); + const TargetInstrInfo &TII = *TM.getSubtargetImpl()->getInstrInfo(); + MachineBasicBlock::iterator I(MI); + ++I; // in any case insert *after* the mul instruction + if (isCopyMulResult(I)) + ++I; + if (isCopyMulResult(I)) + ++I; + BuildMI(*BB, I, MI.getDebugLoc(), TII.get(AVR::EORRdRr), AVR::R1) + .addReg(AVR::R1) + .addReg(AVR::R1); + return BB; +} + +MachineBasicBlock * +AVRTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *MBB) const { + int Opc = MI.getOpcode(); + + // Pseudo shift instructions with a non constant shift amount are expanded + // into a loop. + switch (Opc) { + case AVR::Lsl8: + case AVR::Lsl16: + case AVR::Lsr8: + case AVR::Lsr16: + case AVR::Asr8: + case AVR::Asr16: + return insertShift(MI, MBB); + case AVR::MULRdRr: + case AVR::MULSRdRr: + return insertMul(MI, MBB); + } + + assert((Opc == AVR::Select16 || Opc == AVR::Select8) && + "Unexpected instr type to insert"); + + const AVRInstrInfo &TII = (const AVRInstrInfo &)*MI.getParent() + ->getParent() + ->getSubtarget() + .getInstrInfo(); + DebugLoc dl = MI.getDebugLoc(); + + // To "insert" a SELECT instruction, we insert the diamond + // control-flow pattern. The incoming instruction knows the + // destination vreg to set, the condition code register to branch + // on, the true/false values to select between, and a branch opcode + // to use. + + MachineFunction *MF = MBB->getParent(); + const BasicBlock *LLVM_BB = MBB->getBasicBlock(); + MachineBasicBlock *trueMBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *falseMBB = MF->CreateMachineBasicBlock(LLVM_BB); + + MachineFunction::iterator I = MBB->getParent()->begin(); + ++I; + MF->insert(I, trueMBB); + MF->insert(I, falseMBB); + + // Transfer remaining instructions and all successors of the current + // block to the block which will contain the Phi node for the + // select. + trueMBB->splice(trueMBB->begin(), MBB, + std::next(MachineBasicBlock::iterator(MI)), MBB->end()); + trueMBB->transferSuccessorsAndUpdatePHIs(MBB); + + AVRCC::CondCodes CC = (AVRCC::CondCodes)MI.getOperand(3).getImm(); + BuildMI(MBB, dl, TII.getBrCond(CC)).addMBB(trueMBB); + BuildMI(MBB, dl, TII.get(AVR::RJMPk)).addMBB(falseMBB); + MBB->addSuccessor(falseMBB); + MBB->addSuccessor(trueMBB); + + // Unconditionally flow back to the true block + BuildMI(falseMBB, dl, TII.get(AVR::RJMPk)).addMBB(trueMBB); + falseMBB->addSuccessor(trueMBB); + + // Set up the Phi node to determine where we came from + BuildMI(*trueMBB, trueMBB->begin(), dl, TII.get(AVR::PHI), MI.getOperand(0).getReg()) + .addReg(MI.getOperand(1).getReg()) + .addMBB(MBB) + .addReg(MI.getOperand(2).getReg()) + .addMBB(falseMBB) ; + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return trueMBB; +} + +//===----------------------------------------------------------------------===// +// Inline Asm Support +//===----------------------------------------------------------------------===// + +AVRTargetLowering::ConstraintType +AVRTargetLowering::getConstraintType(StringRef Constraint) const { + if (Constraint.size() == 1) { + // See http://www.nongnu.org/avr-libc/user-manual/inline_asm.html + switch (Constraint[0]) { + case 'a': // Simple upper registers + case 'b': // Base pointer registers pairs + case 'd': // Upper register + case 'l': // Lower registers + case 'e': // Pointer register pairs + case 'q': // Stack pointer register + case 'r': // Any register + case 'w': // Special upper register pairs + return C_RegisterClass; + case 't': // Temporary register + case 'x': case 'X': // Pointer register pair X + case 'y': case 'Y': // Pointer register pair Y + case 'z': case 'Z': // Pointer register pair Z + return C_Register; + case 'Q': // A memory address based on Y or Z pointer with displacement. + return C_Memory; + case 'G': // Floating point constant + case 'I': // 6-bit positive integer constant + case 'J': // 6-bit negative integer constant + case 'K': // Integer constant (Range: 2) + case 'L': // Integer constant (Range: 0) + case 'M': // 8-bit integer constant + case 'N': // Integer constant (Range: -1) + case 'O': // Integer constant (Range: 8, 16, 24) + case 'P': // Integer constant (Range: 1) + case 'R': // Integer constant (Range: -6 to 5)x + return C_Other; + default: + break; + } + } + + return TargetLowering::getConstraintType(Constraint); +} + +unsigned +AVRTargetLowering::getInlineAsmMemConstraint(StringRef ConstraintCode) const { + // Not sure if this is actually the right thing to do, but we got to do + // *something* [agnat] + switch (ConstraintCode[0]) { + case 'Q': + return InlineAsm::Constraint_Q; + } + return TargetLowering::getInlineAsmMemConstraint(ConstraintCode); +} + +AVRTargetLowering::ConstraintWeight +AVRTargetLowering::getSingleConstraintMatchWeight( + AsmOperandInfo &info, const char *constraint) const { + ConstraintWeight weight = CW_Invalid; + Value *CallOperandVal = info.CallOperandVal; + + // If we don't have a value, we can't do a match, + // but allow it at the lowest weight. + // (this behaviour has been copied from the ARM backend) + if (!CallOperandVal) { + return CW_Default; + } + + // Look at the constraint type. + switch (*constraint) { + default: + weight = TargetLowering::getSingleConstraintMatchWeight(info, constraint); + break; + case 'd': + case 'r': + case 'l': + weight = CW_Register; + break; + case 'a': + case 'b': + case 'e': + case 'q': + case 't': + case 'w': + case 'x': case 'X': + case 'y': case 'Y': + case 'z': case 'Z': + weight = CW_SpecificReg; + break; + case 'G': + if (const ConstantFP *C = dyn_cast<ConstantFP>(CallOperandVal)) { + if (C->isZero()) { + weight = CW_Constant; + } + } + break; + case 'I': + if (const ConstantInt *C = dyn_cast<ConstantInt>(CallOperandVal)) { + if (isUInt<6>(C->getZExtValue())) { + weight = CW_Constant; + } + } + break; + case 'J': + if (const ConstantInt *C = dyn_cast<ConstantInt>(CallOperandVal)) { + if ((C->getSExtValue() >= -63) && (C->getSExtValue() <= 0)) { + weight = CW_Constant; + } + } + break; + case 'K': + if (const ConstantInt *C = dyn_cast<ConstantInt>(CallOperandVal)) { + if (C->getZExtValue() == 2) { + weight = CW_Constant; + } + } + break; + case 'L': + if (const ConstantInt *C = dyn_cast<ConstantInt>(CallOperandVal)) { + if (C->getZExtValue() == 0) { + weight = CW_Constant; + } + } + break; + case 'M': + if (const ConstantInt *C = dyn_cast<ConstantInt>(CallOperandVal)) { + if (isUInt<8>(C->getZExtValue())) { + weight = CW_Constant; + } + } + break; + case 'N': + if (const ConstantInt *C = dyn_cast<ConstantInt>(CallOperandVal)) { + if (C->getSExtValue() == -1) { + weight = CW_Constant; + } + } + break; + case 'O': + if (const ConstantInt *C = dyn_cast<ConstantInt>(CallOperandVal)) { + if ((C->getZExtValue() == 8) || (C->getZExtValue() == 16) || + (C->getZExtValue() == 24)) { + weight = CW_Constant; + } + } + break; + case 'P': + if (const ConstantInt *C = dyn_cast<ConstantInt>(CallOperandVal)) { + if (C->getZExtValue() == 1) { + weight = CW_Constant; + } + } + break; + case 'R': + if (const ConstantInt *C = dyn_cast<ConstantInt>(CallOperandVal)) { + if ((C->getSExtValue() >= -6) && (C->getSExtValue() <= 5)) { + weight = CW_Constant; + } + } + break; + case 'Q': + weight = CW_Memory; + break; + } + + return weight; +} + +std::pair<unsigned, const TargetRegisterClass *> +AVRTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, + StringRef Constraint, + MVT VT) const { + auto STI = static_cast<const AVRTargetMachine &>(this->getTargetMachine()) + .getSubtargetImpl(); + + // We only support i8 and i16. + // + //:FIXME: remove this assert for now since it gets sometimes executed + // assert((VT == MVT::i16 || VT == MVT::i8) && "Wrong operand type."); + + if (Constraint.size() == 1) { + switch (Constraint[0]) { + case 'a': // Simple upper registers r16..r23. + return std::make_pair(0U, &AVR::LD8loRegClass); + case 'b': // Base pointer registers: y, z. + return std::make_pair(0U, &AVR::PTRDISPREGSRegClass); + case 'd': // Upper registers r16..r31. + return std::make_pair(0U, &AVR::LD8RegClass); + case 'l': // Lower registers r0..r15. + return std::make_pair(0U, &AVR::GPR8loRegClass); + case 'e': // Pointer register pairs: x, y, z. + return std::make_pair(0U, &AVR::PTRREGSRegClass); + case 'q': // Stack pointer register: SPH:SPL. + return std::make_pair(0U, &AVR::GPRSPRegClass); + case 'r': // Any register: r0..r31. + if (VT == MVT::i8) + return std::make_pair(0U, &AVR::GPR8RegClass); + + assert(VT == MVT::i16 && "inline asm constraint too large"); + return std::make_pair(0U, &AVR::DREGSRegClass); + case 't': // Temporary register: r0. + return std::make_pair(unsigned(AVR::R0), &AVR::GPR8RegClass); + case 'w': // Special upper register pairs: r24, r26, r28, r30. + return std::make_pair(0U, &AVR::IWREGSRegClass); + case 'x': // Pointer register pair X: r27:r26. + case 'X': + return std::make_pair(unsigned(AVR::R27R26), &AVR::PTRREGSRegClass); + case 'y': // Pointer register pair Y: r29:r28. + case 'Y': + return std::make_pair(unsigned(AVR::R29R28), &AVR::PTRREGSRegClass); + case 'z': // Pointer register pair Z: r31:r30. + case 'Z': + return std::make_pair(unsigned(AVR::R31R30), &AVR::PTRREGSRegClass); + default: + break; + } + } + + return TargetLowering::getRegForInlineAsmConstraint(STI->getRegisterInfo(), + Constraint, VT); +} + +void AVRTargetLowering::LowerAsmOperandForConstraint(SDValue Op, + std::string &Constraint, + std::vector<SDValue> &Ops, + SelectionDAG &DAG) const { + SDValue Result(0, 0); + SDLoc DL(Op); + EVT Ty = Op.getValueType(); + + // Currently only support length 1 constraints. + if (Constraint.length() != 1) { + return; + } + + char ConstraintLetter = Constraint[0]; + switch (ConstraintLetter) { + default: + break; + // Deal with integers first: + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'R': { + const ConstantSDNode *C = dyn_cast<ConstantSDNode>(Op); + if (!C) { + return; + } + + int64_t CVal64 = C->getSExtValue(); + uint64_t CUVal64 = C->getZExtValue(); + switch (ConstraintLetter) { + case 'I': // 0..63 + if (!isUInt<6>(CUVal64)) + return; + Result = DAG.getTargetConstant(CUVal64, DL, Ty); + break; + case 'J': // -63..0 + if (CVal64 < -63 || CVal64 > 0) + return; + Result = DAG.getTargetConstant(CVal64, DL, Ty); + break; + case 'K': // 2 + if (CUVal64 != 2) + return; + Result = DAG.getTargetConstant(CUVal64, DL, Ty); + break; + case 'L': // 0 + if (CUVal64 != 0) + return; + Result = DAG.getTargetConstant(CUVal64, DL, Ty); + break; + case 'M': // 0..255 + if (!isUInt<8>(CUVal64)) + return; + // i8 type may be printed as a negative number, + // e.g. 254 would be printed as -2, + // so we force it to i16 at least. + if (Ty.getSimpleVT() == MVT::i8) { + Ty = MVT::i16; + } + Result = DAG.getTargetConstant(CUVal64, DL, Ty); + break; + case 'N': // -1 + if (CVal64 != -1) + return; + Result = DAG.getTargetConstant(CVal64, DL, Ty); + break; + case 'O': // 8, 16, 24 + if (CUVal64 != 8 && CUVal64 != 16 && CUVal64 != 24) + return; + Result = DAG.getTargetConstant(CUVal64, DL, Ty); + break; + case 'P': // 1 + if (CUVal64 != 1) + return; + Result = DAG.getTargetConstant(CUVal64, DL, Ty); + break; + case 'R': // -6..5 + if (CVal64 < -6 || CVal64 > 5) + return; + Result = DAG.getTargetConstant(CVal64, DL, Ty); + break; + } + + break; + } + case 'G': + const ConstantFPSDNode *FC = dyn_cast<ConstantFPSDNode>(Op); + if (!FC || !FC->isZero()) + return; + // Soften float to i8 0 + Result = DAG.getTargetConstant(0, DL, MVT::i8); + break; + } + + if (Result.getNode()) { + Ops.push_back(Result); + return; + } + + return TargetLowering::LowerAsmOperandForConstraint(Op, Constraint, Ops, DAG); +} + +unsigned AVRTargetLowering::getRegisterByName(const char *RegName, + EVT VT, + SelectionDAG &DAG) const { + unsigned Reg; + + if (VT == MVT::i8) { + Reg = StringSwitch<unsigned>(RegName) + .Case("r0", AVR::R0).Case("r1", AVR::R1).Case("r2", AVR::R2) + .Case("r3", AVR::R3).Case("r4", AVR::R4).Case("r5", AVR::R5) + .Case("r6", AVR::R6).Case("r7", AVR::R7).Case("r8", AVR::R8) + .Case("r9", AVR::R9).Case("r10", AVR::R10).Case("r11", AVR::R11) + .Case("r12", AVR::R12).Case("r13", AVR::R13).Case("r14", AVR::R14) + .Case("r15", AVR::R15).Case("r16", AVR::R16).Case("r17", AVR::R17) + .Case("r18", AVR::R18).Case("r19", AVR::R19).Case("r20", AVR::R20) + .Case("r21", AVR::R21).Case("r22", AVR::R22).Case("r23", AVR::R23) + .Case("r24", AVR::R24).Case("r25", AVR::R25).Case("r26", AVR::R26) + .Case("r27", AVR::R27).Case("r28", AVR::R28).Case("r29", AVR::R29) + .Case("r30", AVR::R30).Case("r31", AVR::R31) + .Case("X", AVR::R27R26).Case("Y", AVR::R29R28).Case("Z", AVR::R31R30) + .Default(0); + } else { + Reg = StringSwitch<unsigned>(RegName) + .Case("r0", AVR::R1R0).Case("r2", AVR::R3R2) + .Case("r4", AVR::R5R4).Case("r6", AVR::R7R6) + .Case("r8", AVR::R9R8).Case("r10", AVR::R11R10) + .Case("r12", AVR::R13R12).Case("r14", AVR::R15R14) + .Case("r16", AVR::R17R16).Case("r18", AVR::R19R18) + .Case("r20", AVR::R21R20).Case("r22", AVR::R23R22) + .Case("r24", AVR::R25R24).Case("r26", AVR::R27R26) + .Case("r28", AVR::R29R28).Case("r30", AVR::R31R30) + .Case("X", AVR::R27R26).Case("Y", AVR::R29R28).Case("Z", AVR::R31R30) + .Default(0); + } + + if (Reg) + return Reg; + + report_fatal_error("Invalid register name global variable"); +} + +} // end of namespace llvm + diff --git a/contrib/llvm/lib/Target/AVR/AVRISelLowering.h b/contrib/llvm/lib/Target/AVR/AVRISelLowering.h index 2c8c9c8..a8cdc4e 100644 --- a/contrib/llvm/lib/Target/AVR/AVRISelLowering.h +++ b/contrib/llvm/lib/Target/AVR/AVRISelLowering.h @@ -15,6 +15,7 @@ #ifndef LLVM_AVR_ISEL_LOWERING_H #define LLVM_AVR_ISEL_LOWERING_H +#include "llvm/CodeGen/CallingConvLower.h" #include "llvm/Target/TargetLowering.h" namespace llvm { @@ -92,6 +93,9 @@ public: bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const override; + EVT getSetCCResultType(const DataLayout &DL, LLVMContext &Context, + EVT VT) const override; + MachineBasicBlock * EmitInstrWithCustomInserter(MachineInstr &MI, MachineBasicBlock *MBB) const override; @@ -112,6 +116,9 @@ public: std::vector<SDValue> &Ops, SelectionDAG &DAG) const override; + unsigned getRegisterByName(const char* RegName, EVT VT, + SelectionDAG &DAG) const override; + private: SDValue getAVRCmp(SDValue LHS, SDValue RHS, ISD::CondCode CC, SDValue &AVRcc, SelectionDAG &DAG, SDLoc dl) const; @@ -125,6 +132,13 @@ private: SDValue LowerSETCC(SDValue Op, SelectionDAG &DAG) const; SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) const; + CCAssignFn *CCAssignFnForReturn(CallingConv::ID CC) const; + + bool CanLowerReturn(CallingConv::ID CallConv, + MachineFunction &MF, bool isVarArg, + const SmallVectorImpl<ISD::OutputArg> &Outs, + LLVMContext &Context) const override; + SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool isVarArg, const SmallVectorImpl<ISD::OutputArg> &Outs, const SmallVectorImpl<SDValue> &OutVals, const SDLoc &dl, @@ -143,8 +157,8 @@ private: SmallVectorImpl<SDValue> &InVals) const; private: - MachineBasicBlock *insertShift(MachineInstr *MI, MachineBasicBlock *BB) const; - MachineBasicBlock *insertMul(MachineInstr *MI, MachineBasicBlock *BB) const; + MachineBasicBlock *insertShift(MachineInstr &MI, MachineBasicBlock *BB) const; + MachineBasicBlock *insertMul(MachineInstr &MI, MachineBasicBlock *BB) const; }; } // end namespace llvm diff --git a/contrib/llvm/lib/Target/AVR/AVRInstrFormats.td b/contrib/llvm/lib/Target/AVR/AVRInstrFormats.td index c10023d..ce5e606 100644 --- a/contrib/llvm/lib/Target/AVR/AVRInstrFormats.td +++ b/contrib/llvm/lib/Target/AVR/AVRInstrFormats.td @@ -20,6 +20,8 @@ class AVRInst<dag outs, dag ins, string asmstr, list<dag> pattern> : Instruction dag InOperandList = ins; let AsmString = asmstr; let Pattern = pattern; + + field bits<32> SoftFail = 0; } /// A 16-bit AVR instruction. diff --git a/contrib/llvm/lib/Target/AVR/AVRInstrInfo.cpp b/contrib/llvm/lib/Target/AVR/AVRInstrInfo.cpp index 0327c01..88f8892 100644 --- a/contrib/llvm/lib/Target/AVR/AVRInstrInfo.cpp +++ b/contrib/llvm/lib/Target/AVR/AVRInstrInfo.cpp @@ -27,6 +27,7 @@ #include "AVR.h" #include "AVRMachineFunctionInfo.h" +#include "AVRRegisterInfo.h" #include "AVRTargetMachine.h" #include "MCTargetDesc/AVRMCTargetDesc.h" @@ -42,22 +43,41 @@ void AVRInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, unsigned DestReg, unsigned SrcReg, bool KillSrc) const { + const AVRSubtarget &STI = MBB.getParent()->getSubtarget<AVRSubtarget>(); + const AVRRegisterInfo &TRI = *STI.getRegisterInfo(); unsigned Opc; - if (AVR::GPR8RegClass.contains(DestReg, SrcReg)) { - Opc = AVR::MOVRdRr; - } else if (AVR::DREGSRegClass.contains(DestReg, SrcReg)) { - Opc = AVR::MOVWRdRr; - } else if (SrcReg == AVR::SP && AVR::DREGSRegClass.contains(DestReg)) { - Opc = AVR::SPREAD; - } else if (DestReg == AVR::SP && AVR::DREGSRegClass.contains(SrcReg)) { - Opc = AVR::SPWRITE; + // Not all AVR devices support the 16-bit `MOVW` instruction. + if (AVR::DREGSRegClass.contains(DestReg, SrcReg)) { + if (STI.hasMOVW()) { + BuildMI(MBB, MI, DL, get(AVR::MOVWRdRr), DestReg) + .addReg(SrcReg, getKillRegState(KillSrc)); + } else { + unsigned DestLo, DestHi, SrcLo, SrcHi; + + TRI.splitReg(DestReg, DestLo, DestHi); + TRI.splitReg(SrcReg, SrcLo, SrcHi); + + // Copy each individual register with the `MOV` instruction. + BuildMI(MBB, MI, DL, get(AVR::MOVRdRr), DestLo) + .addReg(SrcLo, getKillRegState(KillSrc)); + BuildMI(MBB, MI, DL, get(AVR::MOVRdRr), DestHi) + .addReg(SrcHi, getKillRegState(KillSrc)); + } } else { - llvm_unreachable("Impossible reg-to-reg copy"); - } + if (AVR::GPR8RegClass.contains(DestReg, SrcReg)) { + Opc = AVR::MOVRdRr; + } else if (SrcReg == AVR::SP && AVR::DREGSRegClass.contains(DestReg)) { + Opc = AVR::SPREAD; + } else if (DestReg == AVR::SP && AVR::DREGSRegClass.contains(SrcReg)) { + Opc = AVR::SPWRITE; + } else { + llvm_unreachable("Impossible reg-to-reg copy"); + } - BuildMI(MBB, MI, DL, get(Opc), DestReg) - .addReg(SrcReg, getKillRegState(KillSrc)); + BuildMI(MBB, MI, DL, get(Opc), DestReg) + .addReg(SrcReg, getKillRegState(KillSrc)); + } } unsigned AVRInstrInfo::isLoadFromStackSlot(const MachineInstr &MI, @@ -105,13 +125,16 @@ void AVRInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB, const TargetRegisterClass *RC, const TargetRegisterInfo *TRI) const { MachineFunction &MF = *MBB.getParent(); + AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>(); + + AFI->setHasSpills(true); DebugLoc DL; if (MI != MBB.end()) { DL = MI->getDebugLoc(); } - const MachineFrameInfo &MFI = *MF.getFrameInfo(); + const MachineFrameInfo &MFI = MF.getFrameInfo(); MachineMemOperand *MMO = MF.getMachineMemOperand( MachinePointerInfo::getFixedStack(MF, FrameIndex), @@ -145,7 +168,7 @@ void AVRInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB, } MachineFunction &MF = *MBB.getParent(); - const MachineFrameInfo &MFI = *MF.getFrameInfo(); + const MachineFrameInfo &MFI = MF.getFrameInfo(); MachineMemOperand *MMO = MF.getMachineMemOperand( MachinePointerInfo::getFixedStack(MF, FrameIndex), @@ -373,13 +396,16 @@ bool AVRInstrInfo::analyzeBranch(MachineBasicBlock &MBB, return false; } -unsigned AVRInstrInfo::InsertBranch(MachineBasicBlock &MBB, +unsigned AVRInstrInfo::insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB, ArrayRef<MachineOperand> Cond, - const DebugLoc &DL) const { + const DebugLoc &DL, + int *BytesAdded) const { + assert(!BytesAdded && "code size not handled"); + // Shouldn't be a fall through. - assert(TBB && "InsertBranch must not be told to insert a fallthrough"); + assert(TBB && "insertBranch must not be told to insert a fallthrough"); assert((Cond.size() == 1 || Cond.size() == 0) && "AVR branch conditions have one component!"); @@ -404,7 +430,10 @@ unsigned AVRInstrInfo::InsertBranch(MachineBasicBlock &MBB, return Count; } -unsigned AVRInstrInfo::RemoveBranch(MachineBasicBlock &MBB) const { +unsigned AVRInstrInfo::removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved) const { + assert(!BytesRemoved && "code size not handled"); + MachineBasicBlock::iterator I = MBB.end(); unsigned Count = 0; @@ -429,7 +458,7 @@ unsigned AVRInstrInfo::RemoveBranch(MachineBasicBlock &MBB) const { return Count; } -bool AVRInstrInfo::ReverseBranchCondition( +bool AVRInstrInfo::reverseBranchCondition( SmallVectorImpl<MachineOperand> &Cond) const { assert(Cond.size() == 1 && "Invalid AVR branch condition!"); @@ -439,8 +468,8 @@ bool AVRInstrInfo::ReverseBranchCondition( return false; } -unsigned AVRInstrInfo::GetInstSizeInBytes(const MachineInstr *MI) const { - unsigned Opcode = MI->getOpcode(); +unsigned AVRInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { + unsigned Opcode = MI.getOpcode(); switch (Opcode) { // A regular instruction @@ -454,13 +483,16 @@ unsigned AVRInstrInfo::GetInstSizeInBytes(const MachineInstr *MI) const { case TargetOpcode::DBG_VALUE: return 0; case TargetOpcode::INLINEASM: { - const MachineFunction *MF = MI->getParent()->getParent(); - const AVRTargetMachine &TM = static_cast<const AVRTargetMachine&>(MF->getTarget()); - const TargetInstrInfo &TII = *TM.getSubtargetImpl()->getInstrInfo(); - return TII.getInlineAsmLength(MI->getOperand(0).getSymbolName(), + const MachineFunction &MF = *MI.getParent()->getParent(); + const AVRTargetMachine &TM = static_cast<const AVRTargetMachine&>(MF.getTarget()); + const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>(); + const TargetInstrInfo &TII = *STI.getInstrInfo(); + + return TII.getInlineAsmLength(MI.getOperand(0).getSymbolName(), *TM.getMCAsmInfo()); } } } } // end of namespace llvm + diff --git a/contrib/llvm/lib/Target/AVR/AVRInstrInfo.h b/contrib/llvm/lib/Target/AVR/AVRInstrInfo.h index fc8945d..c5105da 100644 --- a/contrib/llvm/lib/Target/AVR/AVRInstrInfo.h +++ b/contrib/llvm/lib/Target/AVR/AVRInstrInfo.h @@ -70,7 +70,7 @@ public: const MCInstrDesc &getBrCond(AVRCC::CondCodes CC) const; AVRCC::CondCodes getCondFromBranchOpc(unsigned Opc) const; AVRCC::CondCodes getOppositeCondition(AVRCC::CondCodes CC) const; - unsigned GetInstSizeInBytes(const MachineInstr *MI) const; + unsigned getInstSizeInBytes(const MachineInstr &MI) const override; void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, const DebugLoc &DL, unsigned DestReg, unsigned SrcReg, @@ -94,12 +94,14 @@ public: MachineBasicBlock *&FBB, SmallVectorImpl<MachineOperand> &Cond, bool AllowModify = false) const override; - unsigned InsertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, + unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB, ArrayRef<MachineOperand> Cond, - const DebugLoc &DL) const override; - unsigned RemoveBranch(MachineBasicBlock &MBB) const override; + const DebugLoc &DL, + int *BytesAdded = nullptr) const override; + unsigned removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved = nullptr) const override; bool - ReverseBranchCondition(SmallVectorImpl<MachineOperand> &Cond) const override; + reverseBranchCondition(SmallVectorImpl<MachineOperand> &Cond) const override; private: const AVRRegisterInfo RI; diff --git a/contrib/llvm/lib/Target/AVR/AVRInstrInfo.td b/contrib/llvm/lib/Target/AVR/AVRInstrInfo.td index e756836..bc66379 100644 --- a/contrib/llvm/lib/Target/AVR/AVRInstrInfo.td +++ b/contrib/llvm/lib/Target/AVR/AVRInstrInfo.td @@ -155,7 +155,7 @@ def memspi : Operand<iPTR> let MIOperandInfo = (ops GPRSP, i16imm); } -def i8imm_com : Operand<i8> +def imm_com8 : Operand<i8> { let EncoderMethod = "encodeComplement"; @@ -180,6 +180,38 @@ def call_target : Operand<iPTR> let EncoderMethod = "encodeCallTarget"; } +// A 16-bit address (which can lead to an R_AVR_16 relocation). +def imm16 : Operand<i16> +{ + let EncoderMethod = "encodeImm<AVR::fixup_16>"; +} + +/// A 6-bit immediate used in the ADIW/SBIW instructions. +def imm_arith6 : Operand<i16> +{ + let EncoderMethod = "encodeImm<AVR::fixup_6_adiw>"; +} + +/// An 8-bit immediate inside an instruction with the same format +/// as the `LDI` instruction (the `FRdK` format). +def imm_ldi8 : Operand<i8> +{ + let EncoderMethod = "encodeImm<AVR::fixup_ldi>"; +} + +/// A 5-bit port number used in SBIC and friends (the `FIOBIT` format). +def imm_port5 : Operand<i8> +{ + let EncoderMethod = "encodeImm<AVR::fixup_port5>"; +} + +/// A 6-bit port number used in the `IN` instruction and friends (the +/// `FIORdA` format. +def imm_port6 : Operand<i8> +{ + let EncoderMethod = "encodeImm<AVR::fixup_port6>"; +} + // Addressing mode pattern reg+imm6 def addr : ComplexPattern<iPTR, 2, "SelectAddr", [], [SDNPWantRoot]>; @@ -372,7 +404,7 @@ Defs = [SREG] in // Adds an immediate 6-bit value K to Rd, placing the result in Rd. def ADIWRdK : FWRdK<0b0, (outs IWREGS:$rd), - (ins IWREGS:$src, i16imm:$k), + (ins IWREGS:$src, imm_arith6:$k), "adiw\t$rd, $k", [(set i16:$rd, (add i16:$src, uimm6:$k)), (implicit SREG)]>, @@ -409,7 +441,7 @@ Defs = [SREG] in def SUBIRdK : FRdK<0b0101, (outs LD8:$rd), - (ins LD8:$src, i8imm:$k), + (ins LD8:$src, imm_ldi8:$k), "subi\t$rd, $k", [(set i8:$rd, (sub i8:$src, imm:$k)), (implicit SREG)]>; @@ -427,7 +459,7 @@ Defs = [SREG] in def SBIWRdK : FWRdK<0b1, (outs IWREGS:$rd), - (ins IWREGS:$src, i16imm:$k), + (ins IWREGS:$src, imm_arith6:$k), "sbiw\t$rd, $k", [(set i16:$rd, (sub i16:$src, uimm6:$k)), (implicit SREG)]>, @@ -457,7 +489,7 @@ Defs = [SREG] in def SBCIRdK : FRdK<0b0100, (outs LD8:$rd), - (ins LD8:$src, i8imm:$k), + (ins LD8:$src, imm_ldi8:$k), "sbci\t$rd, $k", [(set i8:$rd, (sube i8:$src, imm:$k)), (implicit SREG)]>; @@ -626,7 +658,7 @@ Defs = [SREG] in def ANDIRdK : FRdK<0b0111, (outs LD8:$rd), - (ins LD8:$src, i8imm:$k), + (ins LD8:$src, imm_ldi8:$k), "andi\t$rd, $k", [(set i8:$rd, (and i8:$src, imm:$k)), (implicit SREG)]>; @@ -644,7 +676,7 @@ Defs = [SREG] in def ORIRdK : FRdK<0b0110, (outs LD8:$rd), - (ins LD8:$src, i8imm:$k), + (ins LD8:$src, imm_ldi8:$k), "ori\t$rd, $k", [(set i8:$rd, (or i8:$src, imm:$k)), (implicit SREG)]>; @@ -871,7 +903,7 @@ let Defs = [SREG] in let Uses = [SREG] in def CPIRdK : FRdK<0b0011, (outs), - (ins GPR8:$rd, i8imm:$k), + (ins GPR8:$rd, imm_ldi8:$k), "cpi\t$rd, $k", [(AVRcmp i8:$rd, imm:$k), (implicit SREG)]>; } @@ -900,13 +932,13 @@ isTerminator = 1 in def SBICAb : FIOBIT<0b01, (outs), - (ins i16imm:$a, i8imm:$b), + (ins imm_port5:$a, i8imm:$b), "sbic\t$a, $b", []>; def SBISAb : FIOBIT<0b11, (outs), - (ins i16imm:$a, i8imm:$b), + (ins imm_port5:$a, i8imm:$b), "sbis\t$a, $b", []>; } @@ -1065,7 +1097,7 @@ let isReMaterializable = 1 in { def LDIRdK : FRdK<0b1110, (outs LD8:$rd), - (ins i8imm:$k), + (ins imm_ldi8:$k), "ldi\t$rd, $k", [(set i8:$rd, imm:$k)]>; @@ -1086,7 +1118,7 @@ isReMaterializable = 1 in { def LDSRdK : F32DM<0b0, (outs GPR8:$rd), - (ins i16imm:$k), + (ins imm16:$k), "lds\t$rd, $k", [(set i8:$rd, (load imm:$k))]>, Requires<[HasSRAM]>; @@ -1175,6 +1207,7 @@ Constraints = "$ptrreg = $base_wb,@earlyclobber $reg,@earlyclobber $base_wb" in let canFoldAsLoad = 1, isReMaterializable = 1 in { + let Constraints = "@earlyclobber $reg" in def LDDRdPtrQ : FSTDLDD<0, (outs GPR8:$reg), (ins memri:$memri), @@ -1194,10 +1227,9 @@ isReMaterializable = 1 in [(set i16:$dst, (load addr:$memri))]>, Requires<[HasSRAM]>; - //:FIXME: remove this once PR13375 gets fixed - // Bug report: https://llvm.org/bugs/show_bug.cgi?id=13375 let mayLoad = 1, - hasSideEffects = 0 in + hasSideEffects = 0, + Constraints = "@earlyclobber $dst" in def LDDWRdYQ : Pseudo<(outs DREGS:$dst), (ins memri:$memri), "lddw\t$dst, $memri", @@ -1205,10 +1237,42 @@ isReMaterializable = 1 in Requires<[HasSRAM]>; } +class AtomicLoad<PatFrag Op, RegisterClass DRC> : + Pseudo<(outs DRC:$rd), (ins PTRREGS:$rr), "atomic_op", + [(set DRC:$rd, (Op i16:$rr))]>; + +class AtomicStore<PatFrag Op, RegisterClass DRC> : + Pseudo<(outs), (ins PTRDISPREGS:$rd, DRC:$rr), "atomic_op", + [(Op i16:$rd, DRC:$rr)]>; + +class AtomicLoadOp<PatFrag Op, RegisterClass DRC> : + Pseudo<(outs DRC:$rd), (ins PTRREGS:$rr, DRC:$operand), + "atomic_op", + [(set DRC:$rd, (Op i16:$rr, DRC:$operand))]>; + +def AtomicLoad8 : AtomicLoad<atomic_load_8, GPR8>; +def AtomicLoad16 : AtomicLoad<atomic_load_16, DREGS>; + +def AtomicStore8 : AtomicStore<atomic_store_8, GPR8>; +def AtomicStore16 : AtomicStore<atomic_store_16, DREGS>; + +def AtomicLoadAdd8 : AtomicLoadOp<atomic_load_add_8, GPR8>; +def AtomicLoadAdd16 : AtomicLoadOp<atomic_load_add_16, DREGS>; +def AtomicLoadSub8 : AtomicLoadOp<atomic_load_sub_8, GPR8>; +def AtomicLoadSub16 : AtomicLoadOp<atomic_load_sub_16, DREGS>; +def AtomicLoadAnd8 : AtomicLoadOp<atomic_load_and_8, GPR8>; +def AtomicLoadAnd16 : AtomicLoadOp<atomic_load_and_16, DREGS>; +def AtomicLoadOr8 : AtomicLoadOp<atomic_load_or_8, GPR8>; +def AtomicLoadOr16 : AtomicLoadOp<atomic_load_or_16, DREGS>; +def AtomicLoadXor8 : AtomicLoadOp<atomic_load_xor_8, GPR8>; +def AtomicLoadXor16 : AtomicLoadOp<atomic_load_xor_16, DREGS>; +def AtomicFence : Pseudo<(outs), (ins), "atomic_fence", + [(atomic_fence imm, imm)]>; + // Indirect store from register to data space. def STSKRr : F32DM<0b1, (outs), - (ins i16imm:$k, GPR8:$rd), + (ins imm16:$k, GPR8:$rd), "sts\t$k, $rd", [(store i8:$rd, imm:$k)]>, Requires<[HasSRAM]>; @@ -1433,24 +1497,24 @@ let canFoldAsLoad = 1, isReMaterializable = 1 in { def INRdA : FIORdA<(outs GPR8:$dst), - (ins i16imm:$src), + (ins imm_port6:$src), "in\t$dst, $src", [(set i8:$dst, (load ioaddr8:$src))]>; def INWRdA : Pseudo<(outs DREGS:$dst), - (ins i16imm:$src), + (ins imm_port6:$src), "inw\t$dst, $src", [(set i16:$dst, (load ioaddr16:$src))]>; } // Write data to IO location operations. def OUTARr : FIOARr<(outs), - (ins i16imm:$dst, GPR8:$src), + (ins imm_port6:$dst, GPR8:$src), "out\t$dst, $src", [(store i8:$src, ioaddr8:$dst)]>; def OUTWARr : Pseudo<(outs), - (ins i16imm:$dst, DREGS:$src), + (ins imm_port6:$dst, DREGS:$src), "outw\t$dst, $src", [(store i16:$src, ioaddr16:$dst)]>; @@ -1613,14 +1677,14 @@ def SWAPRd : FRd<0b1001, // instead of in+ori+out which requires one more instr. def SBIAb : FIOBIT<0b10, (outs), - (ins i16imm:$addr, i8imm:$bit), + (ins imm_port5:$addr, i8imm:$bit), "sbi\t$addr, $bit", [(store (or (i8 (load lowioaddr8:$addr)), iobitpos8:$bit), lowioaddr8:$addr)]>; def CBIAb : FIOBIT<0b00, (outs), - (ins i16imm:$addr, i8imm:$bit), + (ins imm_port5:$addr, i8imm:$bit), "cbi\t$addr, $bit", [(store (and (i8 (load lowioaddr8:$addr)), iobitposn8:$bit), lowioaddr8:$addr)]>; @@ -1648,16 +1712,18 @@ Defs = [SREG] in // Alias for ORI Rd, K def SBRRdK : FRdK<0b0110, (outs LD8:$rd), - (ins LD8:$src, i8imm:$k), + (ins LD8:$src, imm_ldi8:$k), "sbr\t$rd, $k", [(set i8:$rd, (or i8:$src, imm:$k)), (implicit SREG)]>; // CBR Rd, K // Alias for `ANDI Rd, COM(K)` where COM(K) is the compliment of K. + // FIXME: This uses the 'complement' encoder. We need it to also use the + // imm_ldi8 encoder. This will cause no fixups to be created on this instruction. def CBRRdK : FRdK<0b0111, (outs LD8:$rd), - (ins LD8:$src, i8imm_com:$k), + (ins LD8:$src, imm_com8:$k), "cbr\t$rd, $k", []>; } diff --git a/contrib/llvm/lib/Target/AVR/AVRInstrumentFunctions.cpp b/contrib/llvm/lib/Target/AVR/AVRInstrumentFunctions.cpp new file mode 100644 index 0000000..5553dc2 --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/AVRInstrumentFunctions.cpp @@ -0,0 +1,222 @@ +//===-- AVRInstrumentFunctions.cpp - Insert instrumentation for testing ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass takes a function and inserts calls to hook functions which are +// told the name, arguments, and results of function calls. +// +// The hooks can do anything with the information given. It is possible to +// send the data through a serial connection in order to runs tests on +// bare metal. +// +//===----------------------------------------------------------------------===// + +#include "AVR.h" + +#include <llvm/IR/Function.h> +#include <llvm/IR/Module.h> + +using namespace llvm; + +#define AVR_INSTRUMENT_FUNCTIONS_NAME "AVR function instrumentation pass" + +namespace { + +// External symbols that we emit calls to. +namespace symbols { + +#define SYMBOL_PREFIX "avr_instrumentation" + + const StringRef PREFIX = SYMBOL_PREFIX; + + // void (i16 argCount); + const StringRef BEGIN_FUNCTION_SIGNATURE = SYMBOL_PREFIX "_begin_signature"; + // void(i16 argCount); + const StringRef END_FUNCTION_SIGNATURE = SYMBOL_PREFIX "_end_signature"; + +#undef SYMBOL_PREFIX +} + +class AVRInstrumentFunctions : public FunctionPass { +public: + static char ID; + + AVRInstrumentFunctions() : FunctionPass(ID) { + initializeAVRInstrumentFunctionsPass(*PassRegistry::getPassRegistry()); + } + + bool runOnFunction(Function &F) override; + + StringRef getPassName() const override { return AVR_INSTRUMENT_FUNCTIONS_NAME; } +}; + +char AVRInstrumentFunctions::ID = 0; + +/// Creates a pointer to a string. +static Value *CreateStringPtr(BasicBlock &BB, StringRef Str) { + LLVMContext &Ctx = BB.getContext(); + IntegerType *I8 = Type::getInt8Ty(Ctx); + + Constant *ConstantStr = ConstantDataArray::getString(Ctx, Str); + GlobalVariable *GlobalStr = new GlobalVariable(*BB.getParent()->getParent(), + ConstantStr->getType(), + true, /* is a constant */ + GlobalValue::PrivateLinkage, + ConstantStr); + return GetElementPtrInst::CreateInBounds(GlobalStr, + {ConstantInt::get(I8, 0), ConstantInt::get(I8, 0)}, "", &BB); +} + +static std::string GetTypeName(Type &Ty) { + if (auto *IntTy = dyn_cast<IntegerType>(&Ty)) { + return std::string("i") + std::to_string(IntTy->getBitWidth()); + } + + if (Ty.isFloatingPointTy()) { + return std::string("f") + std::to_string(Ty.getPrimitiveSizeInBits()); + } + + llvm_unreachable("unknown return type"); +} + +/// Builds a call to one of the signature begin/end hooks. +static void BuildSignatureCall(StringRef SymName, BasicBlock &BB, Function &F) { + LLVMContext &Ctx = F.getContext(); + IntegerType *I16 = Type::getInt16Ty(Ctx); + + FunctionType *FnType = FunctionType::get(Type::getVoidTy(Ctx), + {Type::getInt8PtrTy(Ctx), I16}, false); + + Constant *Fn = F.getParent()->getOrInsertFunction(SymName, FnType); + Value *FunctionName = CreateStringPtr(BB, F.getName()); + + Value *Args[] = {FunctionName, + ConstantInt::get(I16, F.getArgumentList().size())}; + CallInst::Create(Fn, Args, "", &BB); +} + +/// Builds instructions to call into an external function to +/// notify about a function signature beginning. +static void BuildBeginSignature(BasicBlock &BB, Function &F) { + return BuildSignatureCall(symbols::BEGIN_FUNCTION_SIGNATURE, BB, F); +} + +/// Builds instructions to call into an external function to +/// notify about a function signature ending. +static void BuildEndSignature(BasicBlock &BB, Function &F) { + return BuildSignatureCall(symbols::END_FUNCTION_SIGNATURE, BB, F); +} + +/// Get the name of the external symbol that we need to call +/// to notify about this argument. +static std::string GetArgumentSymbolName(Argument &Arg) { + return (symbols::PREFIX + "_argument_" + GetTypeName(*Arg.getType())).str(); +} + +/// Builds a call to one of the argument hooks. +static void BuildArgument(BasicBlock &BB, Argument &Arg) { + Function &F = *Arg.getParent(); + LLVMContext &Ctx = F.getContext(); + + Type *I8 = Type::getInt8Ty(Ctx); + + FunctionType *FnType = FunctionType::get(Type::getVoidTy(Ctx), + {Type::getInt8PtrTy(Ctx), I8, Arg.getType()}, false); + + Constant *Fn = F.getParent()->getOrInsertFunction( + GetArgumentSymbolName(Arg), FnType); + Value *ArgName = CreateStringPtr(BB, Arg.getName()); + + Value *Args[] = {ArgName, ConstantInt::get(I8, Arg.getArgNo()), &Arg}; + CallInst::Create(Fn, Args, "", &BB); +} + +/// Builds a call to all of the function signature hooks. +static void BuildSignature(BasicBlock &BB, Function &F) { + BuildBeginSignature(BB, F); + for (Argument &Arg : F.args()) { BuildArgument(BB, Arg); } + BuildEndSignature(BB, F); +} + +/// Builds the instrumentation entry block. +static void BuildEntryBlock(Function &F) { + BasicBlock &EntryBlock = F.getEntryBlock(); + + // Create a new basic block at the start of the existing entry block. + BasicBlock *BB = BasicBlock::Create(F.getContext(), + "instrumentation_entry", + &F, &EntryBlock); + + BuildSignature(*BB, F); + + // Jump to the actual entry block. + BranchInst::Create(&EntryBlock, BB); +} + +static std::string GetReturnSymbolName(Value &Val) { + return (symbols::PREFIX + "_result_" + GetTypeName(*Val.getType())).str(); +} + +static void BuildExitHook(Instruction &I) { + Function &F = *I.getParent()->getParent(); + LLVMContext &Ctx = F.getContext(); + + if (auto *Ret = dyn_cast<ReturnInst>(&I)) { + Value *RetVal = Ret->getReturnValue(); + assert(RetVal && "should only be instrumenting functions with return values"); + + FunctionType *FnType = FunctionType::get(Type::getVoidTy(Ctx), + {RetVal->getType()}, false); + + Constant *Fn = F.getParent()->getOrInsertFunction( + GetReturnSymbolName(*RetVal), FnType); + + // Call the result hook just before the return. + CallInst::Create(Fn, {RetVal}, "", &I); + } +} + +/// Runs return hooks before all returns in a function. +static void BuildExitHooks(Function &F) { + for (BasicBlock &BB : F) { + auto BBI = BB.begin(), E = BB.end(); + while (BBI != E) { + auto NBBI = std::next(BBI); + + BuildExitHook(*BBI); + + // Modified |= expandMI(BB, MBBI); + BBI = NBBI; + } + } +} + +static bool ShouldInstrument(Function &F) { + // No point reporting results if there are none. + return !F.getReturnType()->isVoidTy(); +} + +bool AVRInstrumentFunctions::runOnFunction(Function &F) { + if (ShouldInstrument(F)) { + BuildEntryBlock(F); + BuildExitHooks(F); + } + + return true; +} + +} // end of anonymous namespace + +INITIALIZE_PASS(AVRInstrumentFunctions, "avr-instrument-functions", + AVR_INSTRUMENT_FUNCTIONS_NAME, false, false) + +namespace llvm { + +FunctionPass *createAVRInstrumentFunctionsPass() { return new AVRInstrumentFunctions(); } + +} // end of namespace llvm diff --git a/contrib/llvm/lib/Target/AVR/AVRMCInstLower.cpp b/contrib/llvm/lib/Target/AVR/AVRMCInstLower.cpp new file mode 100644 index 0000000..342fe55 --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/AVRMCInstLower.cpp @@ -0,0 +1,100 @@ +//===-- AVRMCInstLower.cpp - Convert AVR MachineInstr to an MCInst --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains code to lower AVR MachineInstrs to their corresponding +// MCInst records. +// +//===----------------------------------------------------------------------===// + +#include "AVRMCInstLower.h" + +#include "AVRInstrInfo.h" +#include "MCTargetDesc/AVRMCExpr.h" + +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/IR/Mangler.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/ErrorHandling.h" + +namespace llvm { + +MCOperand AVRMCInstLower::lowerSymbolOperand(const MachineOperand &MO, + MCSymbol *Sym) const { + unsigned char TF = MO.getTargetFlags(); + const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Ctx); + + bool IsNegated = false; + if (TF & AVRII::MO_NEG) { IsNegated = true; } + + if (!MO.isJTI() && MO.getOffset()) { + Expr = MCBinaryExpr::createAdd( + Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx); + } + + if (TF & AVRII::MO_LO) { + Expr = AVRMCExpr::create(AVRMCExpr::VK_AVR_LO8, Expr, IsNegated, Ctx); + } else if (TF & AVRII::MO_HI) { + Expr = AVRMCExpr::create(AVRMCExpr::VK_AVR_HI8, Expr, IsNegated, Ctx); + } else if (TF != 0) { + llvm_unreachable("Unknown target flag on symbol operand"); + } + + return MCOperand::createExpr(Expr); +} + +void AVRMCInstLower::lowerInstruction(const MachineInstr &MI, MCInst &OutMI) const { + OutMI.setOpcode(MI.getOpcode()); + + for (MachineOperand const &MO : MI.operands()) { + MCOperand MCOp; + + switch (MO.getType()) { + default: + MI.dump(); + llvm_unreachable("unknown operand type"); + case MachineOperand::MO_Register: + // Ignore all implicit register operands. + if (MO.isImplicit()) + continue; + MCOp = MCOperand::createReg(MO.getReg()); + break; + case MachineOperand::MO_Immediate: + MCOp = MCOperand::createImm(MO.getImm()); + break; + case MachineOperand::MO_GlobalAddress: + MCOp = lowerSymbolOperand(MO, Printer.getSymbol(MO.getGlobal())); + break; + case MachineOperand::MO_ExternalSymbol: + MCOp = lowerSymbolOperand( + MO, Printer.GetExternalSymbolSymbol(MO.getSymbolName())); + break; + case MachineOperand::MO_MachineBasicBlock: + MCOp = MCOperand::createExpr( + MCSymbolRefExpr::create(MO.getMBB()->getSymbol(), Ctx)); + break; + case MachineOperand::MO_RegisterMask: + continue; + case MachineOperand::MO_BlockAddress: + MCOp = lowerSymbolOperand( + MO, Printer.GetBlockAddressSymbol(MO.getBlockAddress())); + break; + case MachineOperand::MO_JumpTableIndex: + MCOp = lowerSymbolOperand(MO, Printer.GetJTISymbol(MO.getIndex())); + break; + case MachineOperand::MO_ConstantPoolIndex: + MCOp = lowerSymbolOperand(MO, Printer.GetCPISymbol(MO.getIndex())); + break; + } + + OutMI.addOperand(MCOp); + } +} + +} // end of namespace llvm + diff --git a/contrib/llvm/lib/Target/AVR/AVRMCInstLower.h b/contrib/llvm/lib/Target/AVR/AVRMCInstLower.h new file mode 100644 index 0000000..2e2d101 --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/AVRMCInstLower.h @@ -0,0 +1,43 @@ +//===-- AVRMCInstLower.h - Lower MachineInstr to MCInst ---------*- 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_MCINST_LOWER_H +#define LLVM_AVR_MCINST_LOWER_H + +#include "llvm/Support/Compiler.h" + +namespace llvm { + +class AsmPrinter; +class MachineInstr; +class MachineOperand; +class MCContext; +class MCInst; +class MCOperand; +class MCSymbol; + +/// Lowers `MachineInstr` objects into `MCInst` objects. +class AVRMCInstLower { +public: + AVRMCInstLower(MCContext &Ctx, AsmPrinter &Printer) + : Ctx(Ctx), Printer(Printer) {} + + /// Lowers a `MachineInstr` into a `MCInst`. + void lowerInstruction(const MachineInstr &MI, MCInst &OutMI) const; + MCOperand lowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym) const; + +private: + MCContext &Ctx; + AsmPrinter &Printer; +}; + +} // end namespace llvm + +#endif // LLVM_AVR_MCINST_LOWER_H + diff --git a/contrib/llvm/lib/Target/AVR/AVRRegisterInfo.cpp b/contrib/llvm/lib/Target/AVR/AVRRegisterInfo.cpp index 5786f74..48798bd 100644 --- a/contrib/llvm/lib/Target/AVR/AVRRegisterInfo.cpp +++ b/contrib/llvm/lib/Target/AVR/AVRRegisterInfo.cpp @@ -129,13 +129,13 @@ void AVRRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, const MachineFunction &MF = *MBB.getParent(); const AVRTargetMachine &TM = (const AVRTargetMachine &)MF.getTarget(); const TargetInstrInfo &TII = *TM.getSubtargetImpl()->getInstrInfo(); - const MachineFrameInfo *MFI = MF.getFrameInfo(); + const MachineFrameInfo &MFI = MF.getFrameInfo(); const TargetFrameLowering *TFI = TM.getSubtargetImpl()->getFrameLowering(); int FrameIndex = MI.getOperand(FIOperandNum).getIndex(); - int Offset = MFI->getObjectOffset(FrameIndex); + int Offset = MFI.getObjectOffset(FrameIndex); // Add one to the offset because SP points to an empty slot. - Offset += MFI->getStackSize() - TFI->getOffsetOfLocalArea() + 1; + Offset += MFI.getStackSize() - TFI->getOffsetOfLocalArea() + 1; // Fold incoming offset. Offset += MI.getOperand(FIOperandNum + 1).getImm(); @@ -172,7 +172,7 @@ void AVRRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, Opcode = AVR::ADIWRdK; break; } - // Fallthrough + LLVM_FALLTHROUGH; } default: { // This opcode will get expanded into a pair of subi/sbci. @@ -193,7 +193,7 @@ void AVRRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, // If the offset is too big we have to adjust and restore the frame pointer // to materialize a valid load/store with displacement. //:TODO: consider using only one adiw/sbiw chain for more than one frame index - if (Offset >= 63) { + if (Offset > 63) { unsigned AddOpc = AVR::ADIWRdK, SubOpc = AVR::SBIWRdK; int AddOffset = Offset - 63 + 1; @@ -253,4 +253,14 @@ AVRRegisterInfo::getPointerRegClass(const MachineFunction &MF, return &AVR::PTRDISPREGSRegClass; } +void AVRRegisterInfo::splitReg(unsigned Reg, + unsigned &LoReg, + unsigned &HiReg) const { + assert(AVR::DREGSRegClass.contains(Reg) && "can only split 16-bit registers"); + + LoReg = getSubReg(Reg, AVR::sub_lo); + HiReg = getSubReg(Reg, AVR::sub_hi); +} + } // end of namespace llvm + diff --git a/contrib/llvm/lib/Target/AVR/AVRRegisterInfo.h b/contrib/llvm/lib/Target/AVR/AVRRegisterInfo.h index 59c0849..b97e32e 100644 --- a/contrib/llvm/lib/Target/AVR/AVRRegisterInfo.h +++ b/contrib/llvm/lib/Target/AVR/AVRRegisterInfo.h @@ -42,13 +42,15 @@ public: unsigned FIOperandNum, RegScavenger *RS = NULL) const override; - /// Debug information queries. unsigned getFrameRegister(const MachineFunction &MF) const override; - /// Returns a TargetRegisterClass used for pointer values. const TargetRegisterClass * getPointerRegClass(const MachineFunction &MF, unsigned Kind = 0) const override; + + /// Splits a 16-bit `DREGS` register into the lo/hi register pair. + /// \param Reg A 16-bit register to split. + void splitReg(unsigned Reg, unsigned &LoReg, unsigned &HiReg) const; }; } // end namespace llvm diff --git a/contrib/llvm/lib/Target/AVR/AVRRelaxMemOperations.cpp b/contrib/llvm/lib/Target/AVR/AVRRelaxMemOperations.cpp new file mode 100644 index 0000000..26dbcf7 --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/AVRRelaxMemOperations.cpp @@ -0,0 +1,149 @@ +//===-- AVRRelaxMemOperations.cpp - Relax out of range loads/stores -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains a pass which relaxes out of range memory operations into +// equivalent operations which handle bigger addresses. +// +//===----------------------------------------------------------------------===// + +#include "AVR.h" +#include "AVRInstrInfo.h" +#include "AVRTargetMachine.h" +#include "MCTargetDesc/AVRMCTargetDesc.h" + +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/Target/TargetRegisterInfo.h" + +using namespace llvm; + +#define AVR_RELAX_MEM_OPS_NAME "AVR memory operation relaxation pass" + +namespace { + +class AVRRelaxMem : public MachineFunctionPass { +public: + static char ID; + + AVRRelaxMem() : MachineFunctionPass(ID) { + initializeAVRRelaxMemPass(*PassRegistry::getPassRegistry()); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + + StringRef getPassName() const override { return AVR_RELAX_MEM_OPS_NAME; } + +private: + typedef MachineBasicBlock Block; + typedef Block::iterator BlockIt; + + const TargetInstrInfo *TII; + + template <unsigned OP> bool relax(Block &MBB, BlockIt MBBI); + + bool runOnBasicBlock(Block &MBB); + bool runOnInstruction(Block &MBB, BlockIt MBBI); + + MachineInstrBuilder buildMI(Block &MBB, BlockIt MBBI, unsigned Opcode) { + return BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(Opcode)); + } +}; + +char AVRRelaxMem::ID = 0; + +bool AVRRelaxMem::runOnMachineFunction(MachineFunction &MF) { + bool Modified = false; + + const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>(); + TII = STI.getInstrInfo(); + + for (Block &MBB : MF) { + bool BlockModified = runOnBasicBlock(MBB); + Modified |= BlockModified; + } + + return Modified; +} + +bool AVRRelaxMem::runOnBasicBlock(Block &MBB) { + bool Modified = false; + + BlockIt MBBI = MBB.begin(), E = MBB.end(); + while (MBBI != E) { + BlockIt NMBBI = std::next(MBBI); + Modified |= runOnInstruction(MBB, MBBI); + MBBI = NMBBI; + } + + return Modified; +} + +template <> +bool AVRRelaxMem::relax<AVR::STDWPtrQRr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + + MachineOperand &Ptr = MI.getOperand(0); + MachineOperand &Src = MI.getOperand(2); + int64_t Imm = MI.getOperand(1).getImm(); + + // We can definitely optimise this better. + if (Imm > 63) { + // Push the previous state of the pointer register. + // This instruction must preserve the value. + buildMI(MBB, MBBI, AVR::PUSHWRr) + .addReg(Ptr.getReg()); + + // Add the immediate to the pointer register. + buildMI(MBB, MBBI, AVR::SBCIWRdK) + .addReg(Ptr.getReg(), RegState::Define) + .addReg(Ptr.getReg()) + .addImm(-Imm); + + // Store the value in the source register to the address + // pointed to by the pointer register. + buildMI(MBB, MBBI, AVR::STWPtrRr) + .addReg(Ptr.getReg()) + .addReg(Src.getReg(), getKillRegState(Src.isKill())); + + // Pop the original state of the pointer register. + buildMI(MBB, MBBI, AVR::POPWRd) + .addReg(Ptr.getReg(), getKillRegState(Ptr.isKill())); + + MI.removeFromParent(); + } + + return false; +} + +bool AVRRelaxMem::runOnInstruction(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + int Opcode = MBBI->getOpcode(); + +#define RELAX(Op) \ + case Op: \ + return relax<Op>(MBB, MI) + + switch (Opcode) { + RELAX(AVR::STDWPtrQRr); + } +#undef RELAX + return false; +} + +} // end of anonymous namespace + +INITIALIZE_PASS(AVRRelaxMem, "avr-relax-mem", + AVR_RELAX_MEM_OPS_NAME, false, false) + +namespace llvm { + +FunctionPass *createAVRRelaxMemPass() { return new AVRRelaxMem(); } + +} // end of namespace llvm diff --git a/contrib/llvm/lib/Target/AVR/AVRTargetMachine.cpp b/contrib/llvm/lib/Target/AVR/AVRTargetMachine.cpp index 508723e..fb32629 100644 --- a/contrib/llvm/lib/Target/AVR/AVRTargetMachine.cpp +++ b/contrib/llvm/lib/Target/AVR/AVRTargetMachine.cpp @@ -25,6 +25,8 @@ namespace llvm { +static const char *AVRDataLayout = "e-p:16:16:16-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-n8"; + /// Processes a CPU name. static StringRef getCPU(StringRef CPU) { if (CPU.empty() || CPU == "generic") { @@ -44,7 +46,7 @@ AVRTargetMachine::AVRTargetMachine(const Target &T, const Triple &TT, Optional<Reloc::Model> RM, CodeModel::Model CM, CodeGenOpt::Level OL) : LLVMTargetMachine( - T, "e-p:16:8:8-i8:8:8-i16:8:8-i32:8:8-i64:8:8-f32:8:8-f64:8:8-n8", TT, + T, AVRDataLayout, TT, getCPU(CPU), FS, Options, getEffectiveRelocModel(RM), CM, OL), SubTarget(TT, getCPU(CPU), FS, *this) { this->TLOF = make_unique<AVRTargetObjectFile>(); @@ -65,7 +67,6 @@ public: bool addInstSelector() override; void addPreSched2() override; void addPreRegAlloc() override; - void addPreEmitPass() override; }; } // namespace @@ -75,7 +76,12 @@ TargetPassConfig *AVRTargetMachine::createPassConfig(PassManagerBase &PM) { extern "C" void LLVMInitializeAVRTarget() { // Register the target. - RegisterTargetMachine<AVRTargetMachine> X(TheAVRTarget); + RegisterTargetMachine<AVRTargetMachine> X(getTheAVRTarget()); + + auto &PR = *PassRegistry::getPassRegistry(); + initializeAVRExpandPseudoPass(PR); + initializeAVRInstrumentFunctionsPass(PR); + initializeAVRRelaxMemPass(PR); } const AVRSubtarget *AVRTargetMachine::getSubtargetImpl() const { @@ -91,15 +97,22 @@ const AVRSubtarget *AVRTargetMachine::getSubtargetImpl(const Function &) const { //===----------------------------------------------------------------------===// bool AVRPassConfig::addInstSelector() { + // Install an instruction selector. + addPass(createAVRISelDag(getAVRTargetMachine(), getOptLevel())); + // Create the frame analyzer pass used by the PEI pass. + addPass(createAVRFrameAnalyzerPass()); + return false; } void AVRPassConfig::addPreRegAlloc() { + // Create the dynalloc SP save/restore pass to handle variable sized allocas. + addPass(createAVRDynAllocaSRPass()); } -void AVRPassConfig::addPreSched2() { } - -void AVRPassConfig::addPreEmitPass() { +void AVRPassConfig::addPreSched2() { + addPass(createAVRRelaxMemPass()); + addPass(createAVRExpandPseudoPass()); } } // end of namespace llvm diff --git a/contrib/llvm/lib/Target/AVR/AVRTargetObjectFile.cpp b/contrib/llvm/lib/Target/AVR/AVRTargetObjectFile.cpp index 85f03e8..af14d92 100644 --- a/contrib/llvm/lib/Target/AVR/AVRTargetObjectFile.cpp +++ b/contrib/llvm/lib/Target/AVR/AVRTargetObjectFile.cpp @@ -26,15 +26,16 @@ void AVRTargetObjectFile::Initialize(MCContext &Ctx, const TargetMachine &TM) { } MCSection * -AVRTargetObjectFile::SelectSectionForGlobal(const GlobalValue *GV, - SectionKind Kind, Mangler &Mang, +AVRTargetObjectFile::SelectSectionForGlobal(const GlobalObject *GO, + SectionKind Kind, const TargetMachine &TM) const { // Global values in flash memory are placed in the progmem.data section // unless they already have a user assigned section. - if (AVR::isProgramMemoryAddress(GV) && !GV->hasSection()) + if (AVR::isProgramMemoryAddress(GO) && !GO->hasSection()) return ProgmemDataSection; // Otherwise, we work the same way as ELF. - return Base::SelectSectionForGlobal(GV, Kind, Mang, TM); + return Base::SelectSectionForGlobal(GO, Kind, TM); } } // end of namespace llvm + diff --git a/contrib/llvm/lib/Target/AVR/AVRTargetObjectFile.h b/contrib/llvm/lib/Target/AVR/AVRTargetObjectFile.h index 5876125..ba91036 100644 --- a/contrib/llvm/lib/Target/AVR/AVRTargetObjectFile.h +++ b/contrib/llvm/lib/Target/AVR/AVRTargetObjectFile.h @@ -21,8 +21,7 @@ class AVRTargetObjectFile : public TargetLoweringObjectFileELF { public: void Initialize(MCContext &ctx, const TargetMachine &TM) override; - MCSection *SelectSectionForGlobal(const GlobalValue *GV, SectionKind Kind, - Mangler &Mang, + MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const override; private: diff --git a/contrib/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp b/contrib/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp new file mode 100644 index 0000000..5b0398c --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/AsmParser/AVRAsmParser.cpp @@ -0,0 +1,631 @@ +//===---- AVRAsmParser.cpp - Parse AVR assembly to MCInst instructions ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AVR.h" +#include "AVRRegisterInfo.h" +#include "MCTargetDesc/AVRMCExpr.h" +#include "MCTargetDesc/AVRMCTargetDesc.h" + +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstBuilder.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/MCParser/MCAsmLexer.h" +#include "llvm/MC/MCParser/MCParsedAsmOperand.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/TargetRegistry.h" + +#include <sstream> + +#define DEBUG_TYPE "avr-asm-parser" + +namespace llvm { + +/// Parses AVR assembly from a stream. +class AVRAsmParser : public MCTargetAsmParser { + const MCSubtargetInfo &STI; + MCAsmParser &Parser; + const MCRegisterInfo *MRI; + +#define GET_ASSEMBLER_HEADER +#include "AVRGenAsmMatcher.inc" + + bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, + OperandVector &Operands, MCStreamer &Out, + uint64_t &ErrorInfo, + bool MatchingInlineAsm) override; + + bool ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) override; + + bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name, + SMLoc NameLoc, OperandVector &Operands) override; + + bool ParseDirective(AsmToken directiveID) override; + + OperandMatchResultTy parseMemriOperand(OperandVector &Operands); + + bool parseOperand(OperandVector &Operands); + int parseRegisterName(unsigned (*matchFn)(StringRef)); + int parseRegisterName(); + int parseRegister(); + bool tryParseRegisterOperand(OperandVector &Operands); + bool tryParseExpression(OperandVector &Operands); + bool tryParseRelocExpression(OperandVector &Operands); + void eatComma(); + + unsigned validateTargetOperandClass(MCParsedAsmOperand &Op, + unsigned Kind) override; + + unsigned toDREG(unsigned Reg, unsigned From = AVR::sub_lo) { + MCRegisterClass const *Class = &AVRMCRegisterClasses[AVR::DREGSRegClassID]; + return MRI->getMatchingSuperReg(Reg, From, Class); + } + + bool emit(MCInst &Instruction, SMLoc const &Loc, MCStreamer &Out) const; + bool invalidOperand(SMLoc const &Loc, OperandVector const &Operands, + uint64_t const &ErrorInfo); + bool missingFeature(SMLoc const &Loc, uint64_t const &ErrorInfo); + +public: + AVRAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser, + const MCInstrInfo &MII, const MCTargetOptions &Options) + : MCTargetAsmParser(Options, STI), STI(STI), Parser(Parser) { + MCAsmParserExtension::Initialize(Parser); + MRI = getContext().getRegisterInfo(); + + setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits())); + } + + MCAsmParser &getParser() const { return Parser; } + MCAsmLexer &getLexer() const { return Parser.getLexer(); } +}; + +/// An parsed AVR assembly operand. +class AVROperand : public MCParsedAsmOperand { + typedef MCParsedAsmOperand Base; + enum KindTy { k_Immediate, k_Register, k_Token, k_Memri } Kind; + +public: + AVROperand(StringRef Tok, SMLoc const &S) + : Base(), Kind(k_Token), Tok(Tok), Start(S), End(S) {} + AVROperand(unsigned Reg, SMLoc const &S, SMLoc const &E) + : Base(), Kind(k_Register), RegImm({Reg, nullptr}), Start(S), End(E) {} + AVROperand(MCExpr const *Imm, SMLoc const &S, SMLoc const &E) + : Base(), Kind(k_Immediate), RegImm({0, Imm}), Start(S), End(E) {} + AVROperand(unsigned Reg, MCExpr const *Imm, SMLoc const &S, SMLoc const &E) + : Base(), Kind(k_Memri), RegImm({Reg, Imm}), Start(S), End(E) {} + + struct RegisterImmediate { + unsigned Reg; + MCExpr const *Imm; + }; + union { + StringRef Tok; + RegisterImmediate RegImm; + }; + + SMLoc Start, End; + +public: + void addRegOperands(MCInst &Inst, unsigned N) const { + assert(Kind == k_Register && "Unexpected operand kind"); + assert(N == 1 && "Invalid number of operands!"); + + Inst.addOperand(MCOperand::createReg(getReg())); + } + + void addExpr(MCInst &Inst, const MCExpr *Expr) const { + // Add as immediate when possible + if (!Expr) + Inst.addOperand(MCOperand::createImm(0)); + else if (const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Expr)) + Inst.addOperand(MCOperand::createImm(CE->getValue())); + else + Inst.addOperand(MCOperand::createExpr(Expr)); + } + + void addImmOperands(MCInst &Inst, unsigned N) const { + assert(Kind == k_Immediate && "Unexpected operand kind"); + assert(N == 1 && "Invalid number of operands!"); + + const MCExpr *Expr = getImm(); + addExpr(Inst, Expr); + } + + /// Adds the contained reg+imm operand to an instruction. + void addMemriOperands(MCInst &Inst, unsigned N) const { + assert(Kind == k_Memri && "Unexpected operand kind"); + assert(N == 2 && "Invalid number of operands"); + + Inst.addOperand(MCOperand::createReg(getReg())); + addExpr(Inst, getImm()); + } + + bool isReg() const { return Kind == k_Register; } + bool isImm() const { return Kind == k_Immediate; } + bool isToken() const { return Kind == k_Token; } + bool isMem() const { return Kind == k_Memri; } + bool isMemri() const { return Kind == k_Memri; } + + StringRef getToken() const { + assert(Kind == k_Token && "Invalid access!"); + return Tok; + } + + unsigned getReg() const { + assert((Kind == k_Register || Kind == k_Memri) && "Invalid access!"); + + return RegImm.Reg; + } + + const MCExpr *getImm() const { + assert((Kind == k_Immediate || Kind == k_Memri) && "Invalid access!"); + return RegImm.Imm; + } + + static std::unique_ptr<AVROperand> CreateToken(StringRef Str, SMLoc S) { + return make_unique<AVROperand>(Str, S); + } + + static std::unique_ptr<AVROperand> CreateReg(unsigned RegNum, SMLoc S, + SMLoc E) { + return make_unique<AVROperand>(RegNum, S, E); + } + + static std::unique_ptr<AVROperand> CreateImm(const MCExpr *Val, SMLoc S, + SMLoc E) { + return make_unique<AVROperand>(Val, S, E); + } + + static std::unique_ptr<AVROperand> + CreateMemri(unsigned RegNum, const MCExpr *Val, SMLoc S, SMLoc E) { + return make_unique<AVROperand>(RegNum, Val, S, E); + } + + void makeToken(StringRef Token) { + Kind = k_Token; + Tok = Token; + } + + void makeReg(unsigned RegNo) { + Kind = k_Register; + RegImm = {RegNo, nullptr}; + } + + void makeImm(MCExpr const *Ex) { + Kind = k_Immediate; + RegImm = {0, Ex}; + } + + void makeMemri(unsigned RegNo, MCExpr const *Imm) { + Kind = k_Memri; + RegImm = {RegNo, Imm}; + } + + SMLoc getStartLoc() const { return Start; } + SMLoc getEndLoc() const { return End; } + + virtual void print(raw_ostream &O) const { + switch (Kind) { + case k_Token: + O << "Token: \"" << getToken() << "\""; + break; + case k_Register: + O << "Register: " << getReg(); + break; + case k_Immediate: + O << "Immediate: \"" << *getImm() << "\""; + break; + case k_Memri: { + // only manually print the size for non-negative values, + // as the sign is inserted automatically. + O << "Memri: \"" << getReg() << '+' << *getImm() << "\""; + break; + } + } + O << "\n"; + } +}; + +// Auto-generated Match Functions + +/// Maps from the set of all register names to a register number. +/// \note Generated by TableGen. +static unsigned MatchRegisterName(StringRef Name); + +/// Maps from the set of all alternative registernames to a register number. +/// \note Generated by TableGen. +static unsigned MatchRegisterAltName(StringRef Name); + +bool AVRAsmParser::invalidOperand(SMLoc const &Loc, + OperandVector const &Operands, + uint64_t const &ErrorInfo) { + SMLoc ErrorLoc = Loc; + char const *Diag = 0; + + if (ErrorInfo != ~0U) { + if (ErrorInfo >= Operands.size()) { + Diag = "too few operands for instruction."; + } else { + AVROperand const &Op = (AVROperand const &)*Operands[ErrorInfo]; + + // TODO: See if we can do a better error than just "invalid ...". + if (Op.getStartLoc() != SMLoc()) { + ErrorLoc = Op.getStartLoc(); + } + } + } + + if (!Diag) { + Diag = "invalid operand for instruction"; + } + + return Error(ErrorLoc, Diag); +} + +bool AVRAsmParser::missingFeature(llvm::SMLoc const &Loc, + uint64_t const &ErrorInfo) { + return Error(Loc, "instruction requires a CPU feature not currently enabled"); +} + +bool AVRAsmParser::emit(MCInst &Inst, SMLoc const &Loc, MCStreamer &Out) const { + Inst.setLoc(Loc); + Out.EmitInstruction(Inst, STI); + + return false; +} + +bool AVRAsmParser::MatchAndEmitInstruction(SMLoc Loc, unsigned &Opcode, + OperandVector &Operands, + MCStreamer &Out, uint64_t &ErrorInfo, + bool MatchingInlineAsm) { + MCInst Inst; + unsigned MatchResult = + MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm); + + switch (MatchResult) { + case Match_Success: return emit(Inst, Loc, Out); + case Match_MissingFeature: return missingFeature(Loc, ErrorInfo); + case Match_InvalidOperand: return invalidOperand(Loc, Operands, ErrorInfo); + case Match_MnemonicFail: return Error(Loc, "invalid instruction"); + default: return true; + } +} + +/// Parses a register name using a given matching function. +/// Checks for lowercase or uppercase if necessary. +int AVRAsmParser::parseRegisterName(unsigned (*matchFn)(StringRef)) { + StringRef Name = Parser.getTok().getString(); + + int RegNum = matchFn(Name); + + // GCC supports case insensitive register names. Some of the AVR registers + // are all lower case, some are all upper case but non are mixed. We prefer + // to use the original names in the register definitions. That is why we + // have to test both upper and lower case here. + if (RegNum == AVR::NoRegister) { + RegNum = matchFn(Name.lower()); + } + if (RegNum == AVR::NoRegister) { + RegNum = matchFn(Name.upper()); + } + + return RegNum; +} + +int AVRAsmParser::parseRegisterName() { + int RegNum = parseRegisterName(&MatchRegisterName); + + if (RegNum == AVR::NoRegister) + RegNum = parseRegisterName(&MatchRegisterAltName); + + return RegNum; +} + +int AVRAsmParser::parseRegister() { + int RegNum = AVR::NoRegister; + + if (Parser.getTok().is(AsmToken::Identifier)) { + // Check for register pair syntax + if (Parser.getLexer().peekTok().is(AsmToken::Colon)) { + Parser.Lex(); + Parser.Lex(); // Eat high (odd) register and colon + + if (Parser.getTok().is(AsmToken::Identifier)) { + // Convert lower (even) register to DREG + RegNum = toDREG(parseRegisterName()); + } + } else { + RegNum = parseRegisterName(); + } + } + return RegNum; +} + +bool AVRAsmParser::tryParseRegisterOperand(OperandVector &Operands) { + int RegNo = parseRegister(); + + if (RegNo == AVR::NoRegister) + return true; + + AsmToken const &T = Parser.getTok(); + Operands.push_back(AVROperand::CreateReg(RegNo, T.getLoc(), T.getEndLoc())); + Parser.Lex(); // Eat register token. + + return false; +} + +bool AVRAsmParser::tryParseExpression(OperandVector &Operands) { + SMLoc S = Parser.getTok().getLoc(); + + if (!tryParseRelocExpression(Operands)) + return false; + + if ((Parser.getTok().getKind() == AsmToken::Plus || + Parser.getTok().getKind() == AsmToken::Minus) && + Parser.getLexer().peekTok().getKind() == AsmToken::Identifier) { + // Don't handle this case - it should be split into two + // separate tokens. + return true; + } + + // Parse (potentially inner) expression + MCExpr const *Expression; + if (getParser().parseExpression(Expression)) + return true; + + SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); + Operands.push_back(AVROperand::CreateImm(Expression, S, E)); + return false; +} + +bool AVRAsmParser::tryParseRelocExpression(OperandVector &Operands) { + bool isNegated = false; + AVRMCExpr::VariantKind ModifierKind = AVRMCExpr::VK_AVR_None; + + SMLoc S = Parser.getTok().getLoc(); + + // Check for sign + AsmToken tokens[2]; + size_t ReadCount = Parser.getLexer().peekTokens(tokens); + + if (ReadCount == 2) { + if (tokens[0].getKind() == AsmToken::Identifier && + tokens[1].getKind() == AsmToken::LParen) { + + AsmToken::TokenKind CurTok = Parser.getLexer().getKind(); + if (CurTok == AsmToken::Minus) { + isNegated = true; + } else { + assert(CurTok == AsmToken::Plus); + isNegated = false; + } + + // Eat the sign + Parser.Lex(); + } + } + + // Check if we have a target specific modifier (lo8, hi8, &c) + if (Parser.getTok().getKind() != AsmToken::Identifier || + Parser.getLexer().peekTok().getKind() != AsmToken::LParen) { + // Not a reloc expr + return true; + } + StringRef ModifierName = Parser.getTok().getString(); + ModifierKind = AVRMCExpr::getKindByName(ModifierName.str().c_str()); + + if (ModifierKind != AVRMCExpr::VK_AVR_None) { + Parser.Lex(); + Parser.Lex(); // Eat modifier name and parenthesis + } else { + return Error(Parser.getTok().getLoc(), "unknown modifier"); + } + + MCExpr const *InnerExpression; + if (getParser().parseExpression(InnerExpression)) + return true; + + // If we have a modifier wrap the inner expression + assert(Parser.getTok().getKind() == AsmToken::RParen); + Parser.Lex(); // Eat closing parenthesis + + MCExpr const *Expression = AVRMCExpr::create(ModifierKind, InnerExpression, + isNegated, getContext()); + + SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); + Operands.push_back(AVROperand::CreateImm(Expression, S, E)); + + return false; +} + +bool AVRAsmParser::parseOperand(OperandVector &Operands) { + DEBUG(dbgs() << "parseOperand\n"); + + switch (getLexer().getKind()) { + default: + return Error(Parser.getTok().getLoc(), "unexpected token in operand"); + + case AsmToken::Identifier: + // Try to parse a register, if it fails, + // fall through to the next case. + if (!tryParseRegisterOperand(Operands)) { + return false; + } + case AsmToken::LParen: + case AsmToken::Integer: + case AsmToken::Dot: + return tryParseExpression(Operands); + case AsmToken::Plus: + case AsmToken::Minus: { + // If the sign preceeds a number, parse the number, + // otherwise treat the sign a an independent token. + switch (getLexer().peekTok().getKind()) { + case AsmToken::Integer: + case AsmToken::BigNum: + case AsmToken::Identifier: + case AsmToken::Real: + if (!tryParseExpression(Operands)) + return false; + default: + break; + } + // Treat the token as an independent token. + Operands.push_back(AVROperand::CreateToken(Parser.getTok().getString(), + Parser.getTok().getLoc())); + Parser.Lex(); // Eat the token. + return false; + } + } + + // Could not parse operand + return true; +} + +OperandMatchResultTy +AVRAsmParser::parseMemriOperand(OperandVector &Operands) { + DEBUG(dbgs() << "parseMemriOperand()\n"); + + SMLoc E, S; + MCExpr const *Expression; + int RegNo; + + // Parse register. + { + RegNo = parseRegister(); + + if (RegNo == AVR::NoRegister) + return MatchOperand_ParseFail; + + S = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); + Parser.Lex(); // Eat register token. + } + + // Parse immediate; + { + if (getParser().parseExpression(Expression)) + return MatchOperand_ParseFail; + + E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); + } + + Operands.push_back(AVROperand::CreateMemri(RegNo, Expression, S, E)); + + return MatchOperand_Success; +} + +bool AVRAsmParser::ParseRegister(unsigned &RegNo, SMLoc &StartLoc, + SMLoc &EndLoc) { + StartLoc = Parser.getTok().getLoc(); + RegNo = parseRegister(); + EndLoc = Parser.getTok().getLoc(); + + return (RegNo == AVR::NoRegister); +} + +void AVRAsmParser::eatComma() { + if (getLexer().is(AsmToken::Comma)) { + Parser.Lex(); + } else { + // GCC allows commas to be omitted. + } +} + +bool AVRAsmParser::ParseInstruction(ParseInstructionInfo &Info, + StringRef Mnemonic, SMLoc NameLoc, + OperandVector &Operands) { + Operands.push_back(AVROperand::CreateToken(Mnemonic, NameLoc)); + + bool first = true; + while (getLexer().isNot(AsmToken::EndOfStatement)) { + if (!first) eatComma(); + + first = false; + + auto MatchResult = MatchOperandParserImpl(Operands, Mnemonic); + + if (MatchResult == MatchOperand_Success) { + continue; + } + + if (MatchResult == MatchOperand_ParseFail) { + SMLoc Loc = getLexer().getLoc(); + Parser.eatToEndOfStatement(); + + return Error(Loc, "failed to parse register and immediate pair"); + } + + if (parseOperand(Operands)) { + SMLoc Loc = getLexer().getLoc(); + Parser.eatToEndOfStatement(); + return Error(Loc, "unexpected token in argument list"); + } + } + Parser.Lex(); // Consume the EndOfStatement + return false; +} + +bool AVRAsmParser::ParseDirective(llvm::AsmToken DirectiveID) { return true; } + +extern "C" void LLVMInitializeAVRAsmParser() { + RegisterMCAsmParser<AVRAsmParser> X(getTheAVRTarget()); +} + +#define GET_REGISTER_MATCHER +#define GET_MATCHER_IMPLEMENTATION +#include "AVRGenAsmMatcher.inc" + +// Uses enums defined in AVRGenAsmMatcher.inc +unsigned AVRAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp, + unsigned ExpectedKind) { + AVROperand &Op = static_cast<AVROperand &>(AsmOp); + MatchClassKind Expected = static_cast<MatchClassKind>(ExpectedKind); + + // If need be, GCC converts bare numbers to register names + // It's ugly, but GCC supports it. + if (Op.isImm()) { + if (MCConstantExpr const *Const = dyn_cast<MCConstantExpr>(Op.getImm())) { + int64_t RegNum = Const->getValue(); + std::ostringstream RegName; + RegName << "r" << RegNum; + RegNum = MatchRegisterName(RegName.str().c_str()); + if (RegNum != AVR::NoRegister) { + Op.makeReg(RegNum); + if (validateOperandClass(Op, Expected) == Match_Success) { + return Match_Success; + } + } + // Let the other quirks try their magic. + } + } + + if (Op.isReg()) { + // If the instruction uses a register pair but we got a single, lower + // register we perform a "class cast". + if (isSubclass(Expected, MCK_DREGS)) { + unsigned correspondingDREG = toDREG(Op.getReg()); + + if (correspondingDREG != AVR::NoRegister) { + Op.makeReg(correspondingDREG); + return validateOperandClass(Op, Expected); + } + } + } + return Match_InvalidOperand; +} + +} // end of namespace llvm diff --git a/contrib/llvm/lib/Target/AVR/Disassembler/AVRDisassembler.cpp b/contrib/llvm/lib/Target/AVR/Disassembler/AVRDisassembler.cpp new file mode 100644 index 0000000..d2a21fb --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/Disassembler/AVRDisassembler.cpp @@ -0,0 +1,156 @@ +//===- AVRDisassembler.cpp - Disassembler for AVR ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is part of the AVR Disassembler. +// +//===----------------------------------------------------------------------===// + +#include "AVR.h" +#include "AVRRegisterInfo.h" +#include "AVRSubtarget.h" +#include "MCTargetDesc/AVRMCTargetDesc.h" + +#include "llvm/MC/MCDisassembler/MCDisassembler.h" +#include "llvm/MC/MCFixedLenDisassembler.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +#define DEBUG_TYPE "avr-disassembler" + +typedef MCDisassembler::DecodeStatus DecodeStatus; + +namespace { + +/// A disassembler class for AVR. +class AVRDisassembler : public MCDisassembler { +public: + AVRDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx) + : MCDisassembler(STI, Ctx) {} + virtual ~AVRDisassembler() {} + + DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef<uint8_t> Bytes, uint64_t Address, + raw_ostream &VStream, + raw_ostream &CStream) const override; +}; +} + +static MCDisassembler *createAVRDisassembler(const Target &T, + const MCSubtargetInfo &STI, + MCContext &Ctx) { + return new AVRDisassembler(STI, Ctx); +} + + +extern "C" void LLVMInitializeAVRDisassembler() { + // Register the disassembler. + TargetRegistry::RegisterMCDisassembler(getTheAVRTarget(), + createAVRDisassembler); +} + +static DecodeStatus DecodeGPR8RegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, const void *Decoder) { + return MCDisassembler::Success; +} + +static DecodeStatus DecodeLD8RegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, const void *Decoder) { + return MCDisassembler::Success; +} + +static DecodeStatus DecodePTRREGSRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, const void *Decoder) { + return MCDisassembler::Success; +} + +#include "AVRGenDisassemblerTables.inc" + +static DecodeStatus readInstruction16(ArrayRef<uint8_t> Bytes, uint64_t Address, + uint64_t &Size, uint32_t &Insn) { + if (Bytes.size() < 2) { + Size = 0; + return MCDisassembler::Fail; + } + + Size = 2; + Insn = (Bytes[0] << 0) | (Bytes[1] << 8); + + return MCDisassembler::Success; +} + +static DecodeStatus readInstruction32(ArrayRef<uint8_t> Bytes, uint64_t Address, + uint64_t &Size, uint32_t &Insn) { + + if (Bytes.size() < 4) { + Size = 0; + return MCDisassembler::Fail; + } + + Size = 4; + Insn = (Bytes[0] << 0) | (Bytes[1] << 8) | (Bytes[2] << 16) | (Bytes[3] << 24); + + return MCDisassembler::Success; +} + +static const uint8_t *getDecoderTable(uint64_t Size) { + + switch (Size) { + case 2: return DecoderTable16; + case 4: return DecoderTable32; + default: llvm_unreachable("instructions must be 16 or 32-bits"); + } +} + +DecodeStatus AVRDisassembler::getInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef<uint8_t> Bytes, + uint64_t Address, + raw_ostream &VStream, + raw_ostream &CStream) const { + uint32_t Insn; + + DecodeStatus Result; + + // Try decode a 16-bit instruction. + { + Result = readInstruction16(Bytes, Address, Size, Insn); + + if (Result == MCDisassembler::Fail) return MCDisassembler::Fail; + + // Try to auto-decode a 16-bit instruction. + Result = decodeInstruction(getDecoderTable(Size), Instr, + Insn, Address, this, STI); + + if (Result != MCDisassembler::Fail) + return Result; + } + + // Try decode a 32-bit instruction. + { + Result = readInstruction32(Bytes, Address, Size, Insn); + + if (Result == MCDisassembler::Fail) return MCDisassembler::Fail; + + Result = decodeInstruction(getDecoderTable(Size), Instr, Insn, + Address, this, STI); + + if (Result != MCDisassembler::Fail) { + return Result; + } + + return MCDisassembler::Fail; + } +} + +typedef DecodeStatus (*DecodeFunc)(MCInst &MI, unsigned insn, uint64_t Address, + const void *Decoder); + diff --git a/contrib/llvm/lib/Target/AVR/InstPrinter/AVRInstPrinter.cpp b/contrib/llvm/lib/Target/AVR/InstPrinter/AVRInstPrinter.cpp new file mode 100644 index 0000000..316b783 --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/InstPrinter/AVRInstPrinter.cpp @@ -0,0 +1,171 @@ +//===-- AVRInstPrinter.cpp - Convert AVR MCInst to assembly syntax --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class prints an AVR MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#include "AVRInstPrinter.h" + +#include "MCTargetDesc/AVRMCTargetDesc.h" + +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrDesc.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormattedStream.h" + +#include <cstring> + +#define DEBUG_TYPE "asm-printer" + +namespace llvm { + +// Include the auto-generated portion of the assembly writer. +#define PRINT_ALIAS_INSTR +#include "AVRGenAsmWriter.inc" + +void AVRInstPrinter::printInst(const MCInst *MI, raw_ostream &O, + StringRef Annot, const MCSubtargetInfo &STI) { + unsigned Opcode = MI->getOpcode(); + + // First handle load and store instructions with postinc or predec + // of the form "ld reg, X+". + // TODO: We should be able to rewrite this using TableGen data. + switch (Opcode) { + case AVR::LDRdPtr: + case AVR::LDRdPtrPi: + case AVR::LDRdPtrPd: + O << "\tld\t"; + printOperand(MI, 0, O); + O << ", "; + + if (Opcode == AVR::LDRdPtrPd) + O << '-'; + + printOperand(MI, 1, O); + + if (Opcode == AVR::LDRdPtrPi) + O << '+'; + break; + case AVR::STPtrRr: + O << "\tst\t"; + printOperand(MI, 0, O); + O << ", "; + printOperand(MI, 1, O); + break; + case AVR::STPtrPiRr: + case AVR::STPtrPdRr: + O << "\tst\t"; + + if (Opcode == AVR::STPtrPdRr) + O << '-'; + + printOperand(MI, 1, O); + + if (Opcode == AVR::STPtrPiRr) + O << '+'; + + O << ", "; + printOperand(MI, 2, O); + break; + default: + if (!printAliasInstr(MI, O)) + printInstruction(MI, O); + + printAnnotation(O, Annot); + break; + } +} + +const char *AVRInstPrinter::getPrettyRegisterName(unsigned RegNum, + MCRegisterInfo const &MRI) { + // GCC prints register pairs by just printing the lower register + // If the register contains a subregister, print it instead + if (MRI.getNumSubRegIndices() > 0) { + unsigned RegLoNum = MRI.getSubReg(RegNum, AVR::sub_lo); + RegNum = (RegLoNum != AVR::NoRegister) ? RegLoNum : RegNum; + } + + return getRegisterName(RegNum); +} + +void AVRInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, + raw_ostream &O) { + const MCOperand &Op = MI->getOperand(OpNo); + const MCOperandInfo &MOI = this->MII.get(MI->getOpcode()).OpInfo[OpNo]; + + if (Op.isReg()) { + bool isPtrReg = (MOI.RegClass == AVR::PTRREGSRegClassID) || + (MOI.RegClass == AVR::PTRDISPREGSRegClassID) || + (MOI.RegClass == AVR::ZREGSRegClassID); + + if (isPtrReg) { + O << getRegisterName(Op.getReg(), AVR::ptr); + } else { + O << getPrettyRegisterName(Op.getReg(), MRI); + } + } else if (Op.isImm()) { + O << Op.getImm(); + } else { + assert(Op.isExpr() && "Unknown operand kind in printOperand"); + O << *Op.getExpr(); + } +} + +/// This is used to print an immediate value that ends up +/// being encoded as a pc-relative value. +void AVRInstPrinter::printPCRelImm(const MCInst *MI, unsigned OpNo, + raw_ostream &O) { + const MCOperand &Op = MI->getOperand(OpNo); + + if (Op.isImm()) { + int64_t Imm = Op.getImm(); + O << '.'; + + // Print a position sign if needed. + // Negative values have their sign printed automatically. + if (Imm >= 0) + O << '+'; + + O << Imm; + } else { + assert(Op.isExpr() && "Unknown pcrel immediate operand"); + O << *Op.getExpr(); + } +} + +void AVRInstPrinter::printMemri(const MCInst *MI, unsigned OpNo, + raw_ostream &O) { + assert(MI->getOperand(OpNo).isReg() && "Expected a register for the first operand"); + + const MCOperand &OffsetOp = MI->getOperand(OpNo + 1); + + // Print the register. + printOperand(MI, OpNo, O); + + // Print the {+,-}offset. + if (OffsetOp.isImm()) { + int64_t Offset = OffsetOp.getImm(); + + if (Offset >= 0) + O << '+'; + + O << Offset; + } else if (OffsetOp.isExpr()) { + O << *OffsetOp.getExpr(); + } else { + llvm_unreachable("unknown type for offset"); + } +} + +} // end of namespace llvm + diff --git a/contrib/llvm/lib/Target/AVR/InstPrinter/AVRInstPrinter.h b/contrib/llvm/lib/Target/AVR/InstPrinter/AVRInstPrinter.h new file mode 100644 index 0000000..c9f65b9 --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/InstPrinter/AVRInstPrinter.h @@ -0,0 +1,54 @@ +//===- AVRInstPrinter.h - Convert AVR MCInst to assembly syntax -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class prints an AVR MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_AVR_INST_PRINTER_H +#define LLVM_AVR_INST_PRINTER_H + +#include "llvm/MC/MCInstPrinter.h" + +#include "MCTargetDesc/AVRMCTargetDesc.h" + +namespace llvm { + +/// Prints AVR instructions to a textual stream. +class AVRInstPrinter : public MCInstPrinter { +public: + AVRInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, + const MCRegisterInfo &MRI) + : MCInstPrinter(MAI, MII, MRI) {} + + static const char *getPrettyRegisterName(unsigned RegNo, + MCRegisterInfo const &MRI); + + void printInst(const MCInst *MI, raw_ostream &O, StringRef Annot, + const MCSubtargetInfo &STI) override; + +private: + static const char *getRegisterName(unsigned RegNo, + unsigned AltIdx = AVR::NoRegAltName); + + void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O); + void printPCRelImm(const MCInst *MI, unsigned OpNo, raw_ostream &O); + void printMemri(const MCInst *MI, unsigned OpNo, raw_ostream &O); + + // Autogenerated by TableGen. + void printInstruction(const MCInst *MI, raw_ostream &O); + bool printAliasInstr(const MCInst *MI, raw_ostream &O); + void printCustomAliasOperand(const MCInst *MI, unsigned OpIdx, + unsigned PrintMethodIdx, raw_ostream &O); +}; + +} // end namespace llvm + +#endif // LLVM_AVR_INST_PRINTER_H + diff --git a/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRAsmBackend.cpp b/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRAsmBackend.cpp new file mode 100644 index 0000000..081d8b5 --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRAsmBackend.cpp @@ -0,0 +1,473 @@ +//===-- AVRAsmBackend.cpp - AVR Asm Backend ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the AVRAsmBackend class. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/AVRAsmBackend.h" +#include "MCTargetDesc/AVRFixupKinds.h" +#include "MCTargetDesc/AVRMCTargetDesc.h" + +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDirectives.h" +#include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCFixupKindInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" + +// FIXME: we should be doing checks to make sure asm operands +// are not out of bounds. + +namespace adjust { + +using namespace llvm; + +void signed_width(unsigned Width, uint64_t Value, std::string Description, + const MCFixup &Fixup, MCContext *Ctx = nullptr) { + if (!isIntN(Width, Value)) { + std::string Diagnostic = "out of range " + Description; + + int64_t Min = minIntN(Width); + int64_t Max = maxIntN(Width); + + Diagnostic += " (expected an integer in the range " + std::to_string(Min) + + " to " + std::to_string(Max) + ")"; + + if (Ctx) { + Ctx->reportFatalError(Fixup.getLoc(), Diagnostic); + } else { + llvm_unreachable(Diagnostic.c_str()); + } + } +} + +void unsigned_width(unsigned Width, uint64_t Value, std::string Description, + const MCFixup &Fixup, MCContext *Ctx = nullptr) { + if (!isUIntN(Width, Value)) { + std::string Diagnostic = "out of range " + Description; + + int64_t Max = maxUIntN(Width); + + Diagnostic += " (expected an integer in the range 0 to " + + std::to_string(Max) + ")"; + + if (Ctx) { + Ctx->reportFatalError(Fixup.getLoc(), Diagnostic); + } else { + llvm_unreachable(Diagnostic.c_str()); + } + } +} + +/// Adjusts the value of a branch target before fixup application. +void adjustBranch(unsigned Size, const MCFixup &Fixup, uint64_t &Value, + MCContext *Ctx = nullptr) { + // We have one extra bit of precision because the value is rightshifted by + // one. + unsigned_width(Size + 1, Value, std::string("branch target"), Fixup, Ctx); + + // Rightshifts the value by one. + AVR::fixups::adjustBranchTarget(Value); +} + +/// Adjusts the value of a relative branch target before fixup application. +void adjustRelativeBranch(unsigned Size, const MCFixup &Fixup, uint64_t &Value, + MCContext *Ctx = nullptr) { + // We have one extra bit of precision because the value is rightshifted by + // one. + signed_width(Size + 1, Value, std::string("branch target"), Fixup, Ctx); + + Value -= 2; + + // Rightshifts the value by one. + AVR::fixups::adjustBranchTarget(Value); +} + +/// 22-bit absolute fixup. +/// +/// Resolves to: +/// 1001 kkkk 010k kkkk kkkk kkkk 111k kkkk +/// +/// Offset of 0 (so the result is left shifted by 3 bits before application). +void fixup_call(unsigned Size, const MCFixup &Fixup, uint64_t &Value, + MCContext *Ctx = nullptr) { + adjustBranch(Size, Fixup, Value, Ctx); + + auto top = Value & (0xf00000 << 6); // the top four bits + auto middle = Value & (0x1ffff << 5); // the middle 13 bits + auto bottom = Value & 0x1f; // end bottom 5 bits + + Value = (top << 6) | (middle << 3) | (bottom << 0); +} + +/// 7-bit PC-relative fixup. +/// +/// Resolves to: +/// 0000 00kk kkkk k000 +/// Offset of 0 (so the result is left shifted by 3 bits before application). +void fixup_7_pcrel(unsigned Size, const MCFixup &Fixup, uint64_t &Value, + MCContext *Ctx = nullptr) { + adjustRelativeBranch(Size, Fixup, Value, Ctx); + + // Because the value may be negative, we must mask out the sign bits + Value &= 0x7f; +} + +/// 12-bit PC-relative fixup. +/// Yes, the fixup is 12 bits even though the name says otherwise. +/// +/// Resolves to: +/// 0000 kkkk kkkk kkkk +/// Offset of 0 (so the result isn't left-shifted before application). +void fixup_13_pcrel(unsigned Size, const MCFixup &Fixup, uint64_t &Value, + MCContext *Ctx = nullptr) { + adjustRelativeBranch(Size, Fixup, Value, Ctx); + + // Because the value may be negative, we must mask out the sign bits + Value &= 0xfff; +} + +/// 6-bit fixup for the immediate operand of the ADIW family of +/// instructions. +/// +/// Resolves to: +/// 0000 0000 kk00 kkkk +void fixup_6_adiw(const MCFixup &Fixup, uint64_t &Value, + MCContext *Ctx = nullptr) { + unsigned_width(6, Value, std::string("immediate"), Fixup, Ctx); + + Value = ((Value & 0x30) << 2) | (Value & 0x0f); +} + +/// 5-bit port number fixup on the SBIC family of instructions. +/// +/// Resolves to: +/// 0000 0000 AAAA A000 +void fixup_port5(const MCFixup &Fixup, uint64_t &Value, + MCContext *Ctx = nullptr) { + unsigned_width(5, Value, std::string("port number"), Fixup, Ctx); + + Value &= 0x1f; + + Value <<= 3; +} + +/// 6-bit port number fixup on the `IN` family of instructions. +/// +/// Resolves to: +/// 1011 0AAd dddd AAAA +void fixup_port6(const MCFixup &Fixup, uint64_t &Value, + MCContext *Ctx = nullptr) { + unsigned_width(6, Value, std::string("port number"), Fixup, Ctx); + + Value = ((Value & 0x30) << 5) | (Value & 0x0f); +} + +/// Adjusts a program memory address. +/// This is a simple right-shift. +void pm(uint64_t &Value) { + Value >>= 1; +} + +/// Fixups relating to the LDI instruction. +namespace ldi { + +/// Adjusts a value to fix up the immediate of an `LDI Rd, K` instruction. +/// +/// Resolves to: +/// 0000 KKKK 0000 KKKK +/// Offset of 0 (so the result isn't left-shifted before application). +void fixup(unsigned Size, const MCFixup &Fixup, uint64_t &Value, + MCContext *Ctx = nullptr) { + uint64_t upper = Value & 0xf0; + uint64_t lower = Value & 0x0f; + + Value = (upper << 4) | lower; +} + +void neg(uint64_t &Value) { Value *= -1; } + +void lo8(unsigned Size, const MCFixup &Fixup, uint64_t &Value, + MCContext *Ctx = nullptr) { + Value &= 0xff; + ldi::fixup(Size, Fixup, Value, Ctx); +} + +void hi8(unsigned Size, const MCFixup &Fixup, uint64_t &Value, + MCContext *Ctx = nullptr) { + Value = (Value & 0xff00) >> 8; + ldi::fixup(Size, Fixup, Value, Ctx); +} + +void hh8(unsigned Size, const MCFixup &Fixup, uint64_t &Value, + MCContext *Ctx = nullptr) { + Value = (Value & 0xff0000) >> 16; + ldi::fixup(Size, Fixup, Value, Ctx); +} + +void ms8(unsigned Size, const MCFixup &Fixup, uint64_t &Value, + MCContext *Ctx = nullptr) { + Value = (Value & 0xff000000) >> 24; + ldi::fixup(Size, Fixup, Value, Ctx); +} + +} // end of ldi namespace +} // end of adjust namespace + +namespace llvm { + +// Prepare value for the target space for it +void AVRAsmBackend::adjustFixupValue(const MCFixup &Fixup, uint64_t &Value, + MCContext *Ctx) const { + // The size of the fixup in bits. + uint64_t Size = AVRAsmBackend::getFixupKindInfo(Fixup.getKind()).TargetSize; + + unsigned Kind = Fixup.getKind(); + + switch (Kind) { + default: + llvm_unreachable("unhandled fixup"); + case AVR::fixup_7_pcrel: + adjust::fixup_7_pcrel(Size, Fixup, Value, Ctx); + break; + case AVR::fixup_13_pcrel: + adjust::fixup_13_pcrel(Size, Fixup, Value, Ctx); + break; + case AVR::fixup_call: + adjust::fixup_call(Size, Fixup, Value, Ctx); + break; + case AVR::fixup_ldi: + adjust::ldi::fixup(Size, Fixup, Value, Ctx); + break; + case AVR::fixup_lo8_ldi: + case AVR::fixup_lo8_ldi_pm: + if (Kind == AVR::fixup_lo8_ldi_pm) adjust::pm(Value); + + adjust::ldi::lo8(Size, Fixup, Value, Ctx); + break; + case AVR::fixup_hi8_ldi: + case AVR::fixup_hi8_ldi_pm: + if (Kind == AVR::fixup_hi8_ldi_pm) adjust::pm(Value); + + adjust::ldi::hi8(Size, Fixup, Value, Ctx); + break; + case AVR::fixup_hh8_ldi: + case AVR::fixup_hh8_ldi_pm: + if (Kind == AVR::fixup_hh8_ldi_pm) adjust::pm(Value); + + adjust::ldi::hh8(Size, Fixup, Value, Ctx); + break; + case AVR::fixup_ms8_ldi: + adjust::ldi::ms8(Size, Fixup, Value, Ctx); + break; + + case AVR::fixup_lo8_ldi_neg: + case AVR::fixup_lo8_ldi_pm_neg: + if (Kind == AVR::fixup_lo8_ldi_pm_neg) adjust::pm(Value); + + adjust::ldi::neg(Value); + adjust::ldi::lo8(Size, Fixup, Value, Ctx); + break; + case AVR::fixup_hi8_ldi_neg: + case AVR::fixup_hi8_ldi_pm_neg: + if (Kind == AVR::fixup_hi8_ldi_pm_neg) adjust::pm(Value); + + adjust::ldi::neg(Value); + adjust::ldi::hi8(Size, Fixup, Value, Ctx); + break; + case AVR::fixup_hh8_ldi_neg: + case AVR::fixup_hh8_ldi_pm_neg: + if (Kind == AVR::fixup_hh8_ldi_pm_neg) adjust::pm(Value); + + adjust::ldi::neg(Value); + adjust::ldi::hh8(Size, Fixup, Value, Ctx); + break; + case AVR::fixup_ms8_ldi_neg: + adjust::ldi::neg(Value); + adjust::ldi::ms8(Size, Fixup, Value, Ctx); + break; + case AVR::fixup_16: + adjust::unsigned_width(16, Value, std::string("port number"), Fixup, Ctx); + + Value &= 0xffff; + break; + case AVR::fixup_6_adiw: + adjust::fixup_6_adiw(Fixup, Value, Ctx); + break; + + case AVR::fixup_port5: + adjust::fixup_port5(Fixup, Value, Ctx); + break; + + case AVR::fixup_port6: + adjust::fixup_port6(Fixup, Value, Ctx); + break; + + // Fixups which do not require adjustments. + case FK_Data_2: + case FK_Data_4: + case FK_Data_8: + break; + + case FK_GPRel_4: + llvm_unreachable("don't know how to adjust this fixup"); + break; + } +} + +MCObjectWriter *AVRAsmBackend::createObjectWriter(raw_pwrite_stream &OS) const { + return createAVRELFObjectWriter(OS, + MCELFObjectTargetWriter::getOSABI(OSType)); +} + +void AVRAsmBackend::applyFixup(const MCFixup &Fixup, char *Data, + unsigned DataSize, uint64_t Value, + bool IsPCRel) const { + if (Value == 0) + return; // Doesn't change encoding. + + MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind()); + + // The number of bits in the fixup mask + auto NumBits = Info.TargetSize + Info.TargetOffset; + auto NumBytes = (NumBits / 8) + ((NumBits % 8) == 0 ? 0 : 1); + + // Shift the value into position. + Value <<= Info.TargetOffset; + + unsigned Offset = Fixup.getOffset(); + assert(Offset + NumBytes <= DataSize && "Invalid fixup offset!"); + + // For each byte of the fragment that the fixup touches, mask in the + // bits from the fixup value. + for (unsigned i = 0; i < NumBytes; ++i) { + uint8_t mask = (((Value >> (i * 8)) & 0xff)); + Data[Offset + i] |= mask; + } +} + +MCFixupKindInfo const &AVRAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { + // NOTE: Many AVR fixups work on sets of non-contignous bits. We work around + // this by saying that the fixup is the size of the entire instruction. + const static MCFixupKindInfo Infos[AVR::NumTargetFixupKinds] = { + // This table *must* be in same the order of fixup_* kinds in + // AVRFixupKinds.h. + // + // name offset bits flags + {"fixup_32", 0, 32, 0}, + + {"fixup_7_pcrel", 3, 7, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_13_pcrel", 0, 12, MCFixupKindInfo::FKF_IsPCRel}, + + {"fixup_16", 0, 16, 0}, + {"fixup_16_pm", 0, 16, 0}, + + {"fixup_ldi", 0, 8, 0}, + + {"fixup_lo8_ldi", 0, 8, 0}, + {"fixup_hi8_ldi", 0, 8, 0}, + {"fixup_hh8_ldi", 0, 8, 0}, + {"fixup_ms8_ldi", 0, 8, 0}, + + {"fixup_lo8_ldi_neg", 0, 8, 0}, + {"fixup_hi8_ldi_neg", 0, 8, 0}, + {"fixup_hh8_ldi_neg", 0, 8, 0}, + {"fixup_ms8_ldi_neg", 0, 8, 0}, + + {"fixup_lo8_ldi_pm", 0, 8, 0}, + {"fixup_hi8_ldi_pm", 0, 8, 0}, + {"fixup_hh8_ldi_pm", 0, 8, 0}, + + {"fixup_lo8_ldi_pm_neg", 0, 8, 0}, + {"fixup_hi8_ldi_pm_neg", 0, 8, 0}, + {"fixup_hh8_ldi_pm_neg", 0, 8, 0}, + + {"fixup_call", 0, 22, 0}, + + {"fixup_6", 0, 16, 0}, // non-contiguous + {"fixup_6_adiw", 0, 6, 0}, + + {"fixup_lo8_ldi_gs", 0, 8, 0}, + {"fixup_hi8_ldi_gs", 0, 8, 0}, + + {"fixup_8", 0, 8, 0}, + {"fixup_8_lo8", 0, 8, 0}, + {"fixup_8_hi8", 0, 8, 0}, + {"fixup_8_hlo8", 0, 8, 0}, + + {"fixup_sym_diff", 0, 32, 0}, + {"fixup_16_ldst", 0, 16, 0}, + + {"fixup_lds_sts_16", 0, 16, 0}, + + {"fixup_port6", 0, 16, 0}, // non-contiguous + {"fixup_port5", 3, 5, 0}, + }; + + if (Kind < FirstTargetFixupKind) + return MCAsmBackend::getFixupKindInfo(Kind); + + assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && + "Invalid kind!"); + + return Infos[Kind - FirstTargetFixupKind]; +} + +bool AVRAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const { + // If the count is not 2-byte aligned, we must be writing data into the text + // section (otherwise we have unaligned instructions, and thus have far + // bigger problems), so just write zeros instead. + assert((Count % 2) == 0 && "NOP instructions must be 2 bytes"); + + OW->WriteZeros(Count); + return true; +} + +void AVRAsmBackend::processFixupValue(const MCAssembler &Asm, + const MCAsmLayout &Layout, + const MCFixup &Fixup, + const MCFragment *DF, + const MCValue &Target, uint64_t &Value, + bool &IsResolved) { + switch ((unsigned) Fixup.getKind()) { + // Fixups which should always be recorded as relocations. + case AVR::fixup_7_pcrel: + case AVR::fixup_13_pcrel: + case AVR::fixup_call: + IsResolved = false; + break; + default: + // Parsed LLVM-generated temporary labels are already + // adjusted for instruction size, but normal labels aren't. + // + // To handle both cases, we simply un-adjust the temporary label + // case so it acts like all other labels. + if (Target.getSymA()->getSymbol().isTemporary()) + Value += 2; + + adjustFixupValue(Fixup, Value, &Asm.getContext()); + break; + } +} + +MCAsmBackend *createAVRAsmBackend(const Target &T, const MCRegisterInfo &MRI, + const Triple &TT, StringRef CPU, + const llvm::MCTargetOptions &TO) { + return new AVRAsmBackend(TT.getOS()); +} + +} // end of namespace llvm + diff --git a/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRAsmBackend.h b/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRAsmBackend.h new file mode 100644 index 0000000..7ff4b8f --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRAsmBackend.h @@ -0,0 +1,78 @@ +//===-- AVRAsmBackend.h - AVR Asm Backend --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// \file The AVR assembly backend implementation. +// +//===----------------------------------------------------------------------===// +// + +#ifndef LLVM_AVR_ASM_BACKEND_H +#define LLVM_AVR_ASM_BACKEND_H + +#include "MCTargetDesc/AVRFixupKinds.h" + +#include "llvm/ADT/Triple.h" +#include "llvm/MC/MCAsmBackend.h" + +namespace llvm { + +class MCAssembler; +class MCObjectWriter; +class Target; + +struct MCFixupKindInfo; + +/// Utilities for manipulating generated AVR machine code. +class AVRAsmBackend : public MCAsmBackend { +public: + + AVRAsmBackend(Triple::OSType OSType) + : MCAsmBackend(), OSType(OSType) {} + + void adjustFixupValue(const MCFixup &Fixup, uint64_t &Value, + MCContext *Ctx = nullptr) const; + + MCObjectWriter *createObjectWriter(raw_pwrite_stream &OS) const override; + + void applyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize, + uint64_t Value, bool IsPCRel) const override; + + const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override; + + unsigned getNumFixupKinds() const override { + return AVR::NumTargetFixupKinds; + } + + bool mayNeedRelaxation(const MCInst &Inst) const override { return false; } + + bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, + const MCRelaxableFragment *DF, + const MCAsmLayout &Layout) const override { + llvm_unreachable("RelaxInstruction() unimplemented"); + return false; + } + + void relaxInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, + MCInst &Res) const override {} + + bool writeNopData(uint64_t Count, MCObjectWriter *OW) const override; + + void processFixupValue(const MCAssembler &Asm, const MCAsmLayout &Layout, + const MCFixup &Fixup, const MCFragment *DF, + const MCValue &Target, uint64_t &Value, + bool &IsResolved) override; + +private: + Triple::OSType OSType; +}; + +} // end namespace llvm + +#endif // LLVM_AVR_ASM_BACKEND_H + diff --git a/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRELFObjectWriter.cpp b/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRELFObjectWriter.cpp new file mode 100644 index 0000000..161f305 --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRELFObjectWriter.cpp @@ -0,0 +1,127 @@ +//===-- AVRELFObjectWriter.cpp - AVR ELF Writer ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/AVRFixupKinds.h" +#include "MCTargetDesc/AVRMCTargetDesc.h" + +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCSection.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Support/ErrorHandling.h" + +namespace llvm { + +/// Writes AVR machine code into an ELF32 object file. +class AVRELFObjectWriter : public MCELFObjectTargetWriter { +public: + AVRELFObjectWriter(uint8_t OSABI); + + virtual ~AVRELFObjectWriter() {} + + unsigned getRelocType(MCContext &Ctx, + const MCValue &Target, + const MCFixup &Fixup, + bool IsPCRel) const override; +}; + +AVRELFObjectWriter::AVRELFObjectWriter(uint8_t OSABI) + : MCELFObjectTargetWriter(false, OSABI, ELF::EM_AVR, true, false) {} + +unsigned AVRELFObjectWriter::getRelocType(MCContext &Ctx, + const MCValue &Target, + const MCFixup &Fixup, + bool IsPCRel) const { + switch ((unsigned) Fixup.getKind()) { + case FK_Data_1: + case FK_Data_4: + llvm_unreachable("unsupported relocation type"); + case FK_Data_2: + return ELF::R_AVR_16_PM; + case AVR::fixup_32: + return ELF::R_AVR_32; + case AVR::fixup_7_pcrel: + return ELF::R_AVR_7_PCREL; + case AVR::fixup_13_pcrel: + return ELF::R_AVR_13_PCREL; + case AVR::fixup_16: + return ELF::R_AVR_16; + case AVR::fixup_16_pm: + return ELF::R_AVR_16_PM; + case AVR::fixup_lo8_ldi: + return ELF::R_AVR_LO8_LDI; + case AVR::fixup_hi8_ldi: + return ELF::R_AVR_HI8_LDI; + case AVR::fixup_hh8_ldi: + return ELF::R_AVR_HH8_LDI; + case AVR::fixup_lo8_ldi_neg: + return ELF::R_AVR_LO8_LDI_NEG; + case AVR::fixup_hi8_ldi_neg: + return ELF::R_AVR_HI8_LDI_NEG; + case AVR::fixup_hh8_ldi_neg: + return ELF::R_AVR_HH8_LDI_NEG; + case AVR::fixup_lo8_ldi_pm: + return ELF::R_AVR_LO8_LDI_PM; + case AVR::fixup_hi8_ldi_pm: + return ELF::R_AVR_HI8_LDI_PM; + case AVR::fixup_hh8_ldi_pm: + return ELF::R_AVR_HH8_LDI_PM; + case AVR::fixup_lo8_ldi_pm_neg: + return ELF::R_AVR_LO8_LDI_PM_NEG; + case AVR::fixup_hi8_ldi_pm_neg: + return ELF::R_AVR_HI8_LDI_PM_NEG; + case AVR::fixup_hh8_ldi_pm_neg: + return ELF::R_AVR_HH8_LDI_PM_NEG; + case AVR::fixup_call: + return ELF::R_AVR_CALL; + case AVR::fixup_ldi: + return ELF::R_AVR_LDI; + case AVR::fixup_6: + return ELF::R_AVR_6; + case AVR::fixup_6_adiw: + return ELF::R_AVR_6_ADIW; + case AVR::fixup_ms8_ldi: + return ELF::R_AVR_MS8_LDI; + case AVR::fixup_ms8_ldi_neg: + return ELF::R_AVR_MS8_LDI_NEG; + case AVR::fixup_lo8_ldi_gs: + return ELF::R_AVR_LO8_LDI_GS; + case AVR::fixup_hi8_ldi_gs: + return ELF::R_AVR_HI8_LDI_GS; + case AVR::fixup_8: + return ELF::R_AVR_8; + case AVR::fixup_8_lo8: + return ELF::R_AVR_8_LO8; + case AVR::fixup_8_hi8: + return ELF::R_AVR_8_HI8; + case AVR::fixup_8_hlo8: + return ELF::R_AVR_8_HLO8; + case AVR::fixup_sym_diff: + return ELF::R_AVR_SYM_DIFF; + case AVR::fixup_16_ldst: + return ELF::R_AVR_16_LDST; + case AVR::fixup_lds_sts_16: + return ELF::R_AVR_LDS_STS_16; + case AVR::fixup_port6: + return ELF::R_AVR_PORT6; + case AVR::fixup_port5: + return ELF::R_AVR_PORT5; + default: + llvm_unreachable("invalid fixup kind!"); + } +} + +MCObjectWriter *createAVRELFObjectWriter(raw_pwrite_stream &OS, uint8_t OSABI) { + MCELFObjectTargetWriter *MOTW = new AVRELFObjectWriter(OSABI); + return createELFObjectWriter(MOTW, OS, true); +} + +} // end of namespace llvm + diff --git a/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRFixupKinds.h b/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRFixupKinds.h new file mode 100644 index 0000000..d3bd52d --- /dev/null +++ b/contrib/llvm/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 <x>+<n>, <r>" 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 <typename T> inline void adjustBranchTarget(T &val) { val >>= 1; } + +} // end of namespace fixups +} +} // end of namespace llvm::AVR + +#endif // LLVM_AVR_FIXUP_KINDS_H diff --git a/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.cpp b/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.cpp new file mode 100644 index 0000000..e6dc886 --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.cpp @@ -0,0 +1,304 @@ +//===-- AVRMCCodeEmitter.cpp - Convert AVR Code to Machine Code -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the AVRMCCodeEmitter class. +// +//===----------------------------------------------------------------------===// + +#include "AVRMCCodeEmitter.h" + +#include "MCTargetDesc/AVRMCExpr.h" +#include "MCTargetDesc/AVRMCTargetDesc.h" + +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/raw_ostream.h" + +#define DEBUG_TYPE "mccodeemitter" + +#define GET_INSTRMAP_INFO +#include "AVRGenInstrInfo.inc" +#undef GET_INSTRMAP_INFO + +namespace llvm { + +/// Performs a post-encoding step on a `LD` or `ST` instruction. +/// +/// The encoding of the LD/ST family of instructions is inconsistent w.r.t +/// the pointer register and the addressing mode. +/// +/// The permutations of the format are as followed: +/// ld Rd, X `1001 000d dddd 1100` +/// ld Rd, X+ `1001 000d dddd 1101` +/// ld Rd, -X `1001 000d dddd 1110` +/// +/// ld Rd, Y `1000 000d dddd 1000` +/// ld Rd, Y+ `1001 000d dddd 1001` +/// ld Rd, -Y `1001 000d dddd 1010` +/// +/// ld Rd, Z `1000 000d dddd 0000` +/// ld Rd, Z+ `1001 000d dddd 0001` +/// ld Rd, -Z `1001 000d dddd 0010` +/// ^ +/// | +/// Note this one inconsistent bit - it is 1 sometimes and 0 at other times. +/// There is no logical pattern. Looking at a truth table, the following +/// formula can be derived to fit the pattern: +// +/// ``` +/// inconsistent_bit = is_predec OR is_postinc OR is_reg_x +/// ``` +// +/// We manually set this bit in this post encoder method. +unsigned +AVRMCCodeEmitter::loadStorePostEncoder(const MCInst &MI, unsigned EncodedValue, + const MCSubtargetInfo &STI) const { + + assert(MI.getOperand(0).isReg() && MI.getOperand(1).isReg() && + "the load/store operands must be registers"); + + unsigned Opcode = MI.getOpcode(); + + // check whether either of the registers are the X pointer register. + bool IsRegX = MI.getOperand(0).getReg() == AVR::R27R26 || + MI.getOperand(1).getReg() == AVR::R27R26; + + bool IsPredec = Opcode == AVR::LDRdPtrPd || Opcode == AVR::STPtrPdRr; + bool IsPostinc = Opcode == AVR::LDRdPtrPi || Opcode == AVR::STPtrPiRr; + + // Check if we need to set the inconsistent bit + if (IsRegX || IsPredec || IsPostinc) { + EncodedValue |= (1 << 12); + } + + return EncodedValue; +} + +template <AVR::Fixups Fixup> +unsigned +AVRMCCodeEmitter::encodeRelCondBrTarget(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + + if (MO.isExpr()) { + Fixups.push_back(MCFixup::create(0, MO.getExpr(), + MCFixupKind(Fixup), MI.getLoc())); + return 0; + } + + assert(MO.isImm()); + + // Take the size of the current instruction away. + // With labels, this is implicitly done. + auto target = MO.getImm(); + AVR::fixups::adjustBranchTarget(target); + return target; +} + +unsigned AVRMCCodeEmitter::encodeLDSTPtrReg(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + auto MO = MI.getOperand(OpNo); + + // The operand should be a pointer register. + assert(MO.isReg()); + + switch (MO.getReg()) { + case AVR::R27R26: return 0x03; // X: 0b11 + case AVR::R29R28: return 0x02; // Y: 0b10 + case AVR::R31R30: return 0x00; // Z: 0b00 + default: + llvm_unreachable("invalid pointer register"); + } +} + +/// Encodes a `memri` operand. +/// The operand is 7-bits. +/// * The lower 6 bits is the immediate +/// * The upper bit is the pointer register bit (Z=0,Y=1) +unsigned AVRMCCodeEmitter::encodeMemri(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + auto RegOp = MI.getOperand(OpNo); + auto OffsetOp = MI.getOperand(OpNo + 1); + + assert(RegOp.isReg() && "Expected register operand"); + + uint8_t RegBit = 0; + + switch (RegOp.getReg()) { + default: + llvm_unreachable("Expected either Y or Z register"); + case AVR::R31R30: + RegBit = 0; + break; // Z register + case AVR::R29R28: + RegBit = 1; + break; // Y register + } + + int8_t OffsetBits; + + if (OffsetOp.isImm()) { + OffsetBits = OffsetOp.getImm(); + } else if (OffsetOp.isExpr()) { + OffsetBits = 0; + Fixups.push_back(MCFixup::create(0, OffsetOp.getExpr(), + MCFixupKind(AVR::fixup_6), MI.getLoc())); + } else { + llvm_unreachable("invalid value for offset"); + } + + return (RegBit << 6) | OffsetBits; +} + +unsigned AVRMCCodeEmitter::encodeComplement(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + // The operand should be an immediate. + assert(MI.getOperand(OpNo).isImm()); + + auto Imm = MI.getOperand(OpNo).getImm(); + return (~0) - Imm; +} + +template <AVR::Fixups Fixup> +unsigned AVRMCCodeEmitter::encodeImm(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + auto MO = MI.getOperand(OpNo); + + if (MO.isExpr()) { + if (isa<AVRMCExpr>(MO.getExpr())) { + // If the expression is already an AVRMCExpr (i.e. a lo8(symbol), + // we shouldn't perform any more fixups. Without this check, we would + // instead create a fixup to the symbol named 'lo8(symbol)' which + // is not correct. + return getExprOpValue(MO.getExpr(), Fixups, STI); + } + + MCFixupKind FixupKind = static_cast<MCFixupKind>(Fixup); + Fixups.push_back(MCFixup::create(0, MO.getExpr(), FixupKind, MI.getLoc())); + + return 0; + } + + assert(MO.isImm()); + return MO.getImm(); +} + +unsigned AVRMCCodeEmitter::encodeCallTarget(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + auto MO = MI.getOperand(OpNo); + + if (MO.isExpr()) { + MCFixupKind FixupKind = static_cast<MCFixupKind>(AVR::fixup_call); + Fixups.push_back(MCFixup::create(0, MO.getExpr(), FixupKind, MI.getLoc())); + return 0; + } + + assert(MO.isImm()); + + auto Target = MO.getImm(); + AVR::fixups::adjustBranchTarget(Target); + return Target; +} + +unsigned AVRMCCodeEmitter::getExprOpValue(const MCExpr *Expr, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + + MCExpr::ExprKind Kind = Expr->getKind(); + + if (Kind == MCExpr::Binary) { + Expr = static_cast<const MCBinaryExpr *>(Expr)->getLHS(); + Kind = Expr->getKind(); + } + + if (Kind == MCExpr::Target) { + AVRMCExpr const *AVRExpr = cast<AVRMCExpr>(Expr); + int64_t Result; + if (AVRExpr->evaluateAsConstant(Result)) { + return Result; + } + + MCFixupKind FixupKind = static_cast<MCFixupKind>(AVRExpr->getFixupKind()); + Fixups.push_back(MCFixup::create(0, AVRExpr, FixupKind)); + return 0; + } + + assert(Kind == MCExpr::SymbolRef); + return 0; +} + +unsigned AVRMCCodeEmitter::getMachineOpValue(const MCInst &MI, + const MCOperand &MO, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + if (MO.isReg()) return Ctx.getRegisterInfo()->getEncodingValue(MO.getReg()); + if (MO.isImm()) return static_cast<unsigned>(MO.getImm()); + + if (MO.isFPImm()) + return static_cast<unsigned>(APFloat(MO.getFPImm()) + .bitcastToAPInt() + .getHiBits(32) + .getLimitedValue()); + + // MO must be an Expr. + assert(MO.isExpr()); + + return getExprOpValue(MO.getExpr(), Fixups, STI); +} + +void AVRMCCodeEmitter::emitInstruction(uint64_t Val, unsigned Size, + const MCSubtargetInfo &STI, + raw_ostream &OS) const { + const uint16_t *Words = reinterpret_cast<uint16_t const *>(&Val); + size_t WordCount = Size / 2; + + for (int64_t i = WordCount - 1; i >= 0; --i) { + uint16_t Word = Words[i]; + + OS << (uint8_t) ((Word & 0x00ff) >> 0); + OS << (uint8_t) ((Word & 0xff00) >> 8); + } +} + +void AVRMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + const MCInstrDesc &Desc = MCII.get(MI.getOpcode()); + + // Get byte count of instruction + unsigned Size = Desc.getSize(); + + assert(Size > 0 && "Instruction size cannot be zero"); + + uint64_t BinaryOpCode = getBinaryCodeForInstr(MI, Fixups, STI); + emitInstruction(BinaryOpCode, Size, STI, OS); +} + +MCCodeEmitter *createAVRMCCodeEmitter(const MCInstrInfo &MCII, + const MCRegisterInfo &MRI, + MCContext &Ctx) { + return new AVRMCCodeEmitter(MCII, Ctx); +} + +#include "AVRGenMCCodeEmitter.inc" + +} // end of namespace llvm diff --git a/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.h b/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.h new file mode 100644 index 0000000..5fa425c --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.h @@ -0,0 +1,115 @@ +//===-- AVRMCCodeEmitter.h - Convert AVR Code to Machine Code -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the AVRMCCodeEmitter class. +// +//===----------------------------------------------------------------------===// +// + +#ifndef LLVM_AVR_CODE_EMITTER_H +#define LLVM_AVR_CODE_EMITTER_H + +#include "AVRFixupKinds.h" + +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/Support/DataTypes.h" + +#define GET_INSTRINFO_OPERAND_TYPES_ENUM +#include "AVRGenInstrInfo.inc" + +namespace llvm { + +class MCContext; +class MCExpr; +class MCFixup; +class MCInst; +class MCInstrInfo; +class MCOperand; +class MCSubtargetInfo; +class raw_ostream; + +/// Writes AVR machine code to a stream. +class AVRMCCodeEmitter : public MCCodeEmitter { +public: + AVRMCCodeEmitter(const MCInstrInfo &MCII, MCContext &Ctx) + : MCII(MCII), Ctx(Ctx) {} + +private: + /// Finishes up encoding an LD/ST instruction. + /// The purpose of this function is to set an bit in the instruction + /// which follows no logical pattern. See the implementation for details. + unsigned loadStorePostEncoder(const MCInst &MI, unsigned EncodedValue, + const MCSubtargetInfo &STI) const; + + /// Gets the encoding for a conditional branch target. + template <AVR::Fixups Fixup> + unsigned encodeRelCondBrTarget(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + + /// Encodes the `PTRREGS` operand to a load or store instruction. + unsigned encodeLDSTPtrReg(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + + /// Encodes a `register+immediate` operand for `LDD`/`STD`. + unsigned encodeMemri(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + + /// Takes the compliment of a number (~0 - val). + unsigned encodeComplement(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + + /// Encodes an immediate value with a given fixup. + template <AVR::Fixups Fixup> + unsigned encodeImm(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + + /// Gets the encoding of the target for the `CALL k` instruction. + unsigned encodeCallTarget(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + + /// TableGen'ed function to get the binary encoding for an instruction. + uint64_t getBinaryCodeForInstr(const MCInst &MI, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned getExprOpValue(const MCExpr *Expr, SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + + /// Returns the binary encoding of operand. + /// + /// If the machine operand requires relocation, the relocation is recorded + /// and zero is returned. + unsigned getMachineOpValue(const MCInst &MI, const MCOperand &MO, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + + void emitInstruction(uint64_t Val, unsigned Size, const MCSubtargetInfo &STI, + raw_ostream &OS) const; + + void encodeInstruction(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const override; + + AVRMCCodeEmitter(const AVRMCCodeEmitter &) = delete; + void operator=(const AVRMCCodeEmitter &) = delete; + + const MCInstrInfo &MCII; + MCContext &Ctx; +}; + +} // end namespace of llvm. + +#endif // LLVM_AVR_CODE_EMITTER_H + diff --git a/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRMCExpr.cpp b/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRMCExpr.cpp new file mode 100644 index 0000000..400296b --- /dev/null +++ b/contrib/llvm/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<uint64_t>(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 + diff --git a/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRMCExpr.h b/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRMCExpr.h new file mode 100644 index 0000000..be565a8 --- /dev/null +++ b/contrib/llvm/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 diff --git a/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRMCTargetDesc.cpp b/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRMCTargetDesc.cpp new file mode 100644 index 0000000..a4fa5c0 --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRMCTargetDesc.cpp @@ -0,0 +1,121 @@ +//===-- AVRMCTargetDesc.cpp - AVR Target Descriptions ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides AVR specific target descriptions. +// +//===----------------------------------------------------------------------===// + +#include "AVRELFStreamer.h" +#include "AVRMCAsmInfo.h" +#include "AVRMCTargetDesc.h" +#include "AVRTargetStreamer.h" +#include "InstPrinter/AVRInstPrinter.h" + +#include "llvm/MC/MCELFStreamer.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/TargetRegistry.h" + +#define GET_INSTRINFO_MC_DESC +#include "AVRGenInstrInfo.inc" + +#define GET_SUBTARGETINFO_MC_DESC +#include "AVRGenSubtargetInfo.inc" + +#define GET_REGINFO_MC_DESC +#include "AVRGenRegisterInfo.inc" + +using namespace llvm; + +static MCInstrInfo *createAVRMCInstrInfo() { + MCInstrInfo *X = new MCInstrInfo(); + InitAVRMCInstrInfo(X); + + return X; +} + +static MCRegisterInfo *createAVRMCRegisterInfo(const Triple &TT) { + MCRegisterInfo *X = new MCRegisterInfo(); + InitAVRMCRegisterInfo(X, 0); + + return X; +} + +static MCSubtargetInfo *createAVRMCSubtargetInfo(const Triple &TT, + StringRef CPU, StringRef FS) { + return createAVRMCSubtargetInfoImpl(TT, CPU, FS); +} + +static MCInstPrinter *createAVRMCInstPrinter(const Triple &T, + unsigned SyntaxVariant, + const MCAsmInfo &MAI, + const MCInstrInfo &MII, + const MCRegisterInfo &MRI) { + if (SyntaxVariant == 0) { + return new AVRInstPrinter(MAI, MII, MRI); + } + + return nullptr; +} + +static MCStreamer *createMCStreamer(const Triple &T, MCContext &Context, + MCAsmBackend &MAB, raw_pwrite_stream &OS, + MCCodeEmitter *Emitter, bool RelaxAll) { + return createELFStreamer(Context, MAB, OS, Emitter, RelaxAll); +} + +static MCTargetStreamer * +createAVRObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { + return new AVRELFStreamer(S, STI); +} + +static MCTargetStreamer *createMCAsmTargetStreamer(MCStreamer &S, + formatted_raw_ostream &OS, + MCInstPrinter *InstPrint, + bool isVerboseAsm) { + return new AVRTargetAsmStreamer(S); +} + +extern "C" void LLVMInitializeAVRTargetMC() { + // Register the MC asm info. + RegisterMCAsmInfo<AVRMCAsmInfo> X(getTheAVRTarget()); + + // Register the MC instruction info. + TargetRegistry::RegisterMCInstrInfo(getTheAVRTarget(), createAVRMCInstrInfo); + + // Register the MC register info. + TargetRegistry::RegisterMCRegInfo(getTheAVRTarget(), createAVRMCRegisterInfo); + + // Register the MC subtarget info. + TargetRegistry::RegisterMCSubtargetInfo(getTheAVRTarget(), + createAVRMCSubtargetInfo); + + // Register the MCInstPrinter. + TargetRegistry::RegisterMCInstPrinter(getTheAVRTarget(), + createAVRMCInstPrinter); + + // Register the MC Code Emitter + TargetRegistry::RegisterMCCodeEmitter(getTheAVRTarget(), createAVRMCCodeEmitter); + + // Register the ELF streamer + TargetRegistry::RegisterELFStreamer(getTheAVRTarget(), createMCStreamer); + + // Register the obj target streamer. + TargetRegistry::RegisterObjectTargetStreamer(getTheAVRTarget(), + createAVRObjectTargetStreamer); + + // Register the asm target streamer. + TargetRegistry::RegisterAsmTargetStreamer(getTheAVRTarget(), + createMCAsmTargetStreamer); + + // Register the asm backend (as little endian). + TargetRegistry::RegisterMCAsmBackend(getTheAVRTarget(), createAVRAsmBackend); +} + diff --git a/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRMCTargetDesc.h b/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRMCTargetDesc.h index b72793d..41a5747 100644 --- a/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRMCTargetDesc.h +++ b/contrib/llvm/lib/Target/AVR/MCTargetDesc/AVRMCTargetDesc.h @@ -24,12 +24,13 @@ class MCContext; class MCInstrInfo; class MCObjectWriter; class MCRegisterInfo; +class MCTargetOptions; class StringRef; class Target; class Triple; class raw_pwrite_stream; -extern Target TheAVRTarget; +Target &getTheAVRTarget(); /// Creates a machine code emitter for AVR. MCCodeEmitter *createAVRMCCodeEmitter(const MCInstrInfo &MCII, @@ -38,7 +39,8 @@ MCCodeEmitter *createAVRMCCodeEmitter(const MCInstrInfo &MCII, /// Creates an assembly backend for AVR. MCAsmBackend *createAVRAsmBackend(const Target &T, const MCRegisterInfo &MRI, - const Triple &TT, StringRef CPU); + const Triple &TT, StringRef CPU, + const llvm::MCTargetOptions &TO); /// Creates an ELF object writer for AVR. MCObjectWriter *createAVRELFObjectWriter(raw_pwrite_stream &OS, uint8_t OSABI); diff --git a/contrib/llvm/lib/Target/AVR/README.md b/contrib/llvm/lib/Target/AVR/README.md new file mode 100644 index 0000000..bd8b453 --- /dev/null +++ b/contrib/llvm/lib/Target/AVR/README.md @@ -0,0 +1,8 @@ +# AVR backend + +This experimental backend is for the 8-bit Atmel [AVR](https://en.wikipedia.org/wiki/Atmel_AVR) microcontroller. + +## Useful links + +* [Unresolved bugs](https://llvm.org/bugs/buglist.cgi?product=libraries&component=Backend%3A%20AVR&resolution=---&list_id=109466) +* [Architecture notes](https://github.com/avr-llvm/architecture) diff --git a/contrib/llvm/lib/Target/AVR/TargetInfo/AVRTargetInfo.cpp b/contrib/llvm/lib/Target/AVR/TargetInfo/AVRTargetInfo.cpp index c0e0d20..36cecaa 100644 --- a/contrib/llvm/lib/Target/AVR/TargetInfo/AVRTargetInfo.cpp +++ b/contrib/llvm/lib/Target/AVR/TargetInfo/AVRTargetInfo.cpp @@ -9,17 +9,15 @@ #include "llvm/IR/Module.h" #include "llvm/Support/TargetRegistry.h" - namespace llvm { -Target TheAVRTarget; +Target &getTheAVRTarget() { + static Target TheAVRTarget; + return TheAVRTarget; +} } extern "C" void LLVMInitializeAVRTargetInfo() { - llvm::RegisterTarget<llvm::Triple::avr> X( - llvm::TheAVRTarget, "avr", "Atmel AVR Microcontroller"); + llvm::RegisterTarget<llvm::Triple::avr> X(llvm::getTheAVRTarget(), "avr", + "Atmel AVR Microcontroller"); } -// FIXME: Temporary stub - this function must be defined for linking -// to succeed. Remove once this function is properly implemented. -extern "C" void LLVMInitializeAVRTargetMC() { -} |