[clang] [llvm] [AVR] Handle flash RO data mapped to data space for newer devices (PR #146244)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Jun 28 14:43:09 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-driver
Author: Tom Vijlbrief (tomtor)
<details>
<summary>Changes</summary>
Newer AVR devices map (part of the) flash to a 32kB window at 0x8000 in the data/IO space.
The linker correctly loads readonly data at 0x8000, but we currently always pull in `__do_copy_data` when we encounter RO data.
This fails when we have no other initialized data in `.data` because the copy loop expects to copy at least 1 byte. When the size is `0` it tries to copy 0x10000 bytes.
Newer versions of `avr-ld` (invoked via `avr-gcc`) handle this correctly, and users of newer devices must always install recent `avr-ld` and `avr-lib` versions to gain access to loader spec files and `crt` files. For older devices and older `avr-ld` the old approach remains unchanged.
Also add some support for devices with an `flmap` register (xmega2 and xmega4 with >= 64kB flash).
---
Full diff: https://github.com/llvm/llvm-project/pull/146244.diff
5 Files Affected:
- (modified) clang/include/clang/Driver/Options.td (+4)
- (modified) clang/lib/Basic/Targets/AVR.cpp (+6)
- (modified) clang/lib/Driver/ToolChains/AVR.cpp (+13-2)
- (modified) llvm/lib/Target/AVR/AVRAsmPrinter.cpp (+10-4)
- (modified) llvm/lib/Target/AVR/AVRDevices.td (+34-22)
``````````diff
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 0ffd8c40da7da..3a6f1bb2669a1 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -4886,6 +4886,10 @@ def mguard_EQ : Joined<["-"], "mguard=">, Group<m_Group>,
HelpText<"Enable or disable Control Flow Guard checks and guard tables emission">,
Values<"none,cf,cf-nochecks">;
def mmcu_EQ : Joined<["-"], "mmcu=">, Group<m_Group>;
+def mflmap : Flag<["-"], "mflmap">, Group<m_Group>,
+ HelpText<"Use AVR mapped memory for RO data">;
+def mrodata_in_ram : Flag<["-"], "mrodata-in-ram">, Group<m_Group>,
+ HelpText<"Copy RO data to ram">;
def msim : Flag<["-"], "msim">, Group<m_Group>;
def mfix_and_continue : Flag<["-"], "mfix-and-continue">, Group<clang_ignored_m_Group>;
def mieee_fp : Flag<["-"], "mieee-fp">, Group<clang_ignored_m_Group>;
diff --git a/clang/lib/Basic/Targets/AVR.cpp b/clang/lib/Basic/Targets/AVR.cpp
index bbe7b01ca036d..448e72206776f 100644
--- a/clang/lib/Basic/Targets/AVR.cpp
+++ b/clang/lib/Basic/Targets/AVR.cpp
@@ -418,6 +418,10 @@ static MCUInfo AVRMcus[] = {
} // namespace targets
} // namespace clang
+static bool ArchHasFLMAP(StringRef Name) {
+ return Name.starts_with("avr64") or Name.starts_with("avr128");
+}
+
static bool ArchHasELPM(StringRef Arch) {
return llvm::StringSwitch<bool>(Arch)
.Cases("31", "51", "6", true)
@@ -529,6 +533,8 @@ void AVRTargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__AVR_ARCH__", Arch);
// TODO: perhaps we should use the information from AVRDevices.td instead?
+ if (ArchHasFLMAP(DefineName))
+ Builder.defineMacro("__AVR_HAVE_FLMAP__");
if (ArchHasELPM(Arch))
Builder.defineMacro("__AVR_HAVE_ELPM__");
if (ArchHasELPMX(Arch))
diff --git a/clang/lib/Driver/ToolChains/AVR.cpp b/clang/lib/Driver/ToolChains/AVR.cpp
index 731076d9754a9..155e0d812c95f 100644
--- a/clang/lib/Driver/ToolChains/AVR.cpp
+++ b/clang/lib/Driver/ToolChains/AVR.cpp
@@ -651,8 +651,19 @@ void AVR::Linker::ConstructJob(Compilation &C, const JobAction &JA,
// This is almost always required because otherwise avr-ld
// will assume 'avr2' and warn about the program being larger
// than the bare minimum supports.
- if (Linker.find("avr-ld") != std::string::npos && FamilyName)
- CmdArgs.push_back(Args.MakeArgString(std::string("-m") + *FamilyName));
+ if (Linker.find("avr-ld") != std::string::npos && FamilyName) {
+ // Option to use mapped memory for modern devices with >32kB flash.
+ // This is the only option for modern devices with <= 32kB flash,
+ // but the larger default to a copy from flash to RAM (avr-ld version < 14)
+ // or map the highest 32kB to RAM (avr-ld version >= 14).
+ if (Args.hasFlag(options::OPT_mflmap, options::OPT_mrodata_in_ram, false)) {
+ CmdArgs.push_back(Args.MakeArgString(std::string("-m") + *FamilyName + "_flmap"));
+ CmdArgs.push_back(Args.MakeArgString(std::string("-u")));
+ CmdArgs.push_back(Args.MakeArgString(std::string("__do_flmap_init")));
+ }
+ else
+ CmdArgs.push_back(Args.MakeArgString(std::string("-m") + *FamilyName));
+ }
C.addCommand(std::make_unique<Command>(
JA, *this, ResponseFileSupport::AtFileCurCP(), Args.MakeArgString(Linker),
diff --git a/llvm/lib/Target/AVR/AVRAsmPrinter.cpp b/llvm/lib/Target/AVR/AVRAsmPrinter.cpp
index ad8aa5717fb42..decfcc4b67f5d 100644
--- a/llvm/lib/Target/AVR/AVRAsmPrinter.cpp
+++ b/llvm/lib/Target/AVR/AVRAsmPrinter.cpp
@@ -263,11 +263,17 @@ bool AVRAsmPrinter::doFinalization(Module &M) {
auto *Section = cast<MCSectionELF>(TLOF.SectionForGlobal(&GO, TM));
if (Section->getName().starts_with(".data"))
NeedsCopyData = true;
- else if (Section->getName().starts_with(".rodata") && SubTM->hasLPM())
+ else if (Section->getName().starts_with(".rodata") && SubTM->hasLPM()) {
// AVRs that have a separate program memory (that's most AVRs) store
- // .rodata sections in RAM.
- NeedsCopyData = true;
- else if (Section->getName().starts_with(".bss"))
+ // .rodata sections in RAM,
+ // but XMEGA3 family maps all flash in the data space.
+ // Invoking __do_copy_data with 0 bytes to copy will crash,
+ // so we let the loader handle this for newer devices.
+ if (!(SubTM->hasFeatureSetFamilyXMEGA2() ||
+ SubTM->hasFeatureSetFamilyXMEGA3() ||
+ SubTM->hasFeatureSetFamilyXMEGA4()))
+ NeedsCopyData = true;
+ } else if (Section->getName().starts_with(".bss"))
NeedsClearBSS = true;
}
diff --git a/llvm/lib/Target/AVR/AVRDevices.td b/llvm/lib/Target/AVR/AVRDevices.td
index ad760d7403573..0b6b0e3696e5f 100644
--- a/llvm/lib/Target/AVR/AVRDevices.td
+++ b/llvm/lib/Target/AVR/AVRDevices.td
@@ -49,6 +49,16 @@ def FeatureEIJMPCALL : SubtargetFeature<"eijmpcall", "HasEIJMPCALL", "true",
"The device supports the "
"`EIJMP`/`EICALL` instructions">;
+// The device supports `FLMAP` flash bank data mapping to the data space.
+def FeatureFLMAP : SubtargetFeature<"flmap", "HasFLMAP", "true",
+ "The device supports the "
+ "`FLMAP` mapping">;
+
+// The device maps all flash (<= 32kB) to the data space.
+def FeatureMAP : SubtargetFeature<"map", "HasMAP", "true",
+ "The device maps all flash (<= 32kB) "
+ "to the data space">;
+
// The device supports `ADDI Rd, K`, `SUBI Rd, K`.
def FeatureADDSUBIW : SubtargetFeature<"addsubiw", "HasADDSUBIW", "true",
"Enable 16-bit register-immediate "
@@ -220,6 +230,7 @@ def FamilyXMEGA3 : Family<"xmega3",
[FamilyAVR0, FeatureLPM, FeatureIJMPCALL,
FeatureADDSUBIW, FeatureSRAM, FeatureJMPCALL,
FeatureMultiplication, FeatureMOVW, FeatureLPMX,
+ FeatureMAP,
FeatureBREAK, FeatureLowByteFirst]>;
def FamilyXMEGA4 : Family<"xmega4",
@@ -228,6 +239,7 @@ def FamilyXMEGA4 : Family<"xmega4",
FeatureMultiplication, FeatureMOVW, FeatureLPMX,
FeatureELPM, FeatureELPMX,
FeatureSPM, FeatureSPMX,
+ FeatureFLMAP,
FeatureBREAK, FeatureLowByteFirst]>;
def FamilyXMEGA : Family<"xmega",
@@ -275,9 +287,9 @@ 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<"avrxmega2", FamilyXMEGA2, ELFArchXMEGA2>;
def : Device<"avrxmega3", FamilyXMEGA3, ELFArchXMEGA3>;
-def : Device<"avrxmega4", FamilyXMEGA, ELFArchXMEGA4>;
+def : Device<"avrxmega4", FamilyXMEGA4, ELFArchXMEGA4>;
def : Device<"avrxmega5", FamilyXMEGA, ELFArchXMEGA5>;
def : Device<"avrxmega6", FamilyXMEGA, ELFArchXMEGA6>;
def : Device<"avrxmega7", FamilyXMEGA, ELFArchXMEGA7>;
@@ -596,26 +608,26 @@ def : Device<"atmega4809", FamilyXMEGA3, ELFArchXMEGA3>;
// Additions from gcc 14:
-def : Device<"avr64da28", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64da32", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64da48", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64da64", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64db28", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64db32", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64db48", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64db64", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64dd14", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64dd20", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64dd28", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64dd32", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64du28", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64du32", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64ea28", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64ea32", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64ea48", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64sd28", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64sd32", FamilyXMEGA2, ELFArchXMEGA2>;
-def : Device<"avr64sd48", FamilyXMEGA2, ELFArchXMEGA2>;
+def : Device<"avr64da28", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64da32", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64da48", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64da64", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64db28", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64db32", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64db48", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64db64", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64dd14", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64dd20", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64dd28", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64dd32", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64du28", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64du32", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64ea28", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64ea32", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64ea48", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64sd28", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64sd32", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
+def : Device<"avr64sd48", FamilyXMEGA2, ELFArchXMEGA2, [FeatureFLMAP]>;
def : Device<"avr16dd20", FamilyXMEGA3, ELFArchXMEGA3>;
def : Device<"avr16dd28", FamilyXMEGA3, ELFArchXMEGA3>;
``````````
</details>
https://github.com/llvm/llvm-project/pull/146244
More information about the llvm-commits
mailing list