diff --git a/llvm/include/llvm/TargetParser/AVRTargetParser.h b/llvm/include/llvm/TargetParser/AVRTargetParser.h new file mode 100644 index 0000000000000..6a44448d7b046 --- /dev/null +++ b/llvm/include/llvm/TargetParser/AVRTargetParser.h @@ -0,0 +1,28 @@ +//===-- AVRTargetParser - Parser for AVR target features ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a target parser to recognise AVR hardware features. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TARGETPARSER_AVRTARGETPARSER_H +#define LLVM_TARGETPARSER_AVRTARGETPARSER_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include + +namespace llvm { +namespace AVR { + +LLVM_ABI Expected getFeatureSetFromEFlag(const unsigned EFlag); + +} // namespace AVR +} // namespace llvm +#endif diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h index 9480e7b36dc2c..3207c85c485da 100644 --- a/llvm/include/llvm/TargetParser/Triple.h +++ b/llvm/include/llvm/TargetParser/Triple.h @@ -1036,6 +1036,8 @@ class Triple { : PointerWidth == 64; } + bool isAVR() const { return getArch() == Triple::avr; } + /// Tests whether the target is 32-bit LoongArch. bool isLoongArch32() const { return getArch() == Triple::loongarch32; } diff --git a/llvm/lib/TargetParser/AVRTargetParser.cpp b/llvm/lib/TargetParser/AVRTargetParser.cpp new file mode 100644 index 0000000000000..ef4a5cf09c20f --- /dev/null +++ b/llvm/lib/TargetParser/AVRTargetParser.cpp @@ -0,0 +1,50 @@ +//===-- AVRTargetParser - Parser for AVR target features ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a target parser to recognise AVR hardware features. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/TargetParser/AVRTargetParser.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Support/Errc.h" + +using namespace llvm; + +Expected AVR::getFeatureSetFromEFlag(const unsigned EFlag) { + static const DenseMap EFlagToFeatureSet = { + {ELF::EF_AVR_ARCH_AVR1, "avr1"}, + {ELF::EF_AVR_ARCH_AVR2, "avr2"}, + {ELF::EF_AVR_ARCH_AVR25, "avr25"}, + {ELF::EF_AVR_ARCH_AVR3, "avr3"}, + {ELF::EF_AVR_ARCH_AVR31, "avr31"}, + {ELF::EF_AVR_ARCH_AVR35, "avr35"}, + {ELF::EF_AVR_ARCH_AVR4, "avr4"}, + {ELF::EF_AVR_ARCH_AVR5, "avr5"}, + {ELF::EF_AVR_ARCH_AVR51, "avr51"}, + {ELF::EF_AVR_ARCH_AVR6, "avr6"}, + {ELF::EF_AVR_ARCH_AVRTINY, "avrtiny"}, + {ELF::EF_AVR_ARCH_XMEGA1, "xmega1"}, + {ELF::EF_AVR_ARCH_XMEGA2, "xmega2"}, + {ELF::EF_AVR_ARCH_XMEGA3, "xmega3"}, + {ELF::EF_AVR_ARCH_XMEGA4, "xmega4"}, + {ELF::EF_AVR_ARCH_XMEGA5, "xmega"}, + {ELF::EF_AVR_ARCH_XMEGA6, "xmega"}, + {ELF::EF_AVR_ARCH_XMEGA7, "xmega"}, + }; + + auto It = EFlagToFeatureSet.find(EFlag); + if (It != EFlagToFeatureSet.end()) + return It->second.str(); + + return createStringError(errc::invalid_argument, + "unrecognised AVR version, 0x" + + Twine::utohexstr(EFlag)); +} diff --git a/llvm/lib/TargetParser/CMakeLists.txt b/llvm/lib/TargetParser/CMakeLists.txt index e1a30199e1ade..8d474497f5308 100644 --- a/llvm/lib/TargetParser/CMakeLists.txt +++ b/llvm/lib/TargetParser/CMakeLists.txt @@ -17,6 +17,7 @@ add_llvm_component_library(LLVMTargetParser AArch64TargetParser.cpp ARMTargetParserCommon.cpp ARMTargetParser.cpp + AVRTargetParser.cpp CSKYTargetParser.cpp Host.cpp LoongArchTargetParser.cpp diff --git a/llvm/test/tools/llvm-objdump/ELF/AVR/lit.local.cfg b/llvm/test/tools/llvm-objdump/ELF/AVR/lit.local.cfg new file mode 100644 index 0000000000000..1724fb233c169 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ELF/AVR/lit.local.cfg @@ -0,0 +1,2 @@ +if not "AVR" in config.root.targets: + config.unsupported = True diff --git a/llvm/test/tools/llvm-objdump/ELF/AVR/mattr.test b/llvm/test/tools/llvm-objdump/ELF/AVR/mattr.test new file mode 100644 index 0000000000000..40b8f34bb6767 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ELF/AVR/mattr.test @@ -0,0 +1,55 @@ +## When --mattr and --mcpu are both empty, disassemble ELF with e_flags derived feature. +# RUN: yaml2obj -DARCH_ARCHVERSION=EF_AVR_ARCH_AVR1 %s -o %t.avr1 +# RUN: llvm-objdump -d %t.avr1 | FileCheck %s --check-prefix=AVR1 +# RUN: yaml2obj -DARCH_ARCHVERSION=EF_AVR_ARCH_AVR3 %s -o %t.avr3 +# RUN: llvm-objdump -d %t.avr3 | FileCheck %s --check-prefix=ALL + +## The mask is being correctly applied before the version is determined. +# RUN: yaml2obj -DARCH_ARCHVERSION='EF_AVR_ARCH_AVR3, EF_AVR_LINKRELAX_PREPARED' %s -o %t.multiflag +# RUN: llvm-objdump -d %t.multiflag | FileCheck %s --check-prefix=ALL + +## If --mattr or --mcpu is specified, don't default to the file's e_flags derived feature. +# RUN: llvm-objdump -d --mattr=+avr3 %t.avr1 | FileCheck %s --check-prefix=ALL +# RUN: llvm-objdump -d --mcpu=avr3 %t.avr1 | FileCheck %s --check-prefix=ALL +# RUN: llvm-objdump -d --mattr=+avr1 %t.avr3 | FileCheck %s --check-prefix=AVR1 +# RUN: llvm-objdump -d --mcpu=avr1 %t.avr3 | FileCheck %s --check-prefix=AVR1 + +## If the file's e_flags doesn't representing an AVR version, default to "avr0". +# RUN: yaml2obj -DARCH_ARCHVERSION=EF_AVR_LINKRELAX_PREPARED %s -o %t.noarchversion +# RUN: llvm-objdump -d %t.noarchversion 2>&1 | FileCheck -DFILE=%t.noarchversion -DVERSION=0x0 %s --check-prefixes=AVR0,INVALIDARCHINFO + +## If file's e_flags is unrecognised, default to "avr0". +# RUN: yaml2obj -DARCH_ARCHVERSION='EF_AVR_ARCH_AVR1, EF_AVR_ARCH_AVR6' %s -o %t.invalid +# RUN: llvm-objdump -d %t.invalid 2>&1 | FileCheck -DFILE=%t.invalid -DVERSION=0x7 %s --check-prefixes=AVR0,INVALIDARCHINFO + +# AVR1: <_start>: +# AVR1-COUNT-2: +# AVR1-NEXT: lpm +# AVR1-NEXT: rjmp .-2 + +# ALL: <_start>: +# ALL-NEXT: call 0x0 +# ALL-NEXT: jmp 0x0 +# ALL-NEXT: lpm +# ALL-NEXT: rjmp .-2 + +# INVALIDARCHINFO: warning: '[[FILE]]': unrecognised AVR version, [[VERSION]]: defaulting to avr0 +# AVR0: <_start>: +# AVR0-COUNT-3: + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_AVR + Flags: [ [[ARCH_ARCHVERSION]] ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Content: 0E9400000C940000C895FFCF +Symbols: + - Name: _start + Section: .text + Binding: STB_GLOBAL diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp index 97032eca5f2d1..6d0bc1a5942a5 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -73,6 +73,7 @@ #include "llvm/Support/TargetSelect.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/TargetParser/AVRTargetParser.h" #include "llvm/TargetParser/Host.h" #include "llvm/TargetParser/Triple.h" #include @@ -2643,6 +2644,21 @@ static void disassembleObject(ObjectFile *Obj, bool InlineRelocs, Features.AddFeature(MAttrs[I]); } else if (MCPU.empty() && Obj->makeTriple().isAArch64()) { Features.AddFeature("+all"); + } else if (MCPU.empty() && Obj->makeTriple().isAVR()) { + if (const auto *Elf = dyn_cast(Obj)) { + if (Expected VersionOrErr = AVR::getFeatureSetFromEFlag( + Elf->getPlatformFlags() & ELF::EF_AVR_ARCH_MASK)) { + Features.AddFeature('+' + *VersionOrErr); + } else { + // If the architecture version cannot be determined from ELF flags, + // fall back to the baseline "avr0" ISA. The AVR disassembler + // requires a valid feature specification to function correctly. + reportWarning(toString(VersionOrErr.takeError()) + + ": defaulting to avr0", + Obj->getFileName()); + Features.AddFeature("+avr0"); + } + } } if (MCPU.empty())