mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
fix(ldgen): correctly place symbols generated by compiler during IPA
As part of inter-procedural optimizations (IPA), the compiler may
perform tasks such as constant propagation for functions. This involves
generating a specialized version of a given function with a new symbol
name that includes a suffix. For example, during constant propagation,
the compiler might create a specialized version named
`spiflash_start_core.constprop.0` for the `spiflash_start_core`
function. Additionally, the compiler may generate multiple clones of a
single function. Currently, when ldgen performs symbol placement, it
does not account for these compiler-generated functions, leading to
their incorrect or unexpected placement in memory (markers).
Consider a linker fragment with:
```
[mapping:spi_flash]
archive: libspi_flash.a
entries:
esp_flash_api: spiflash_start_core (noflash)
```
The `spiflash_start_core` function should be placed in IRAM. However,
the compiler might generate an optimized version of this function with a
`.constprop.0` suffix, resulting in a
`.text.spiflash_start_core.constprop.0` input section. Currently, ldgen
does not handle this situation, leading to misplaced symbols.
Since `.` is not allowed in C identifiers, it should be safe to consider
all input sections for a symbol with any `.` suffix as representing that
symbol. This means considering the symbol suffixes should not cause any
ambiguity.
This change automatically places all input sections, including those
with possible suffixes for a given symbol, into the specified memory. In
other words, specifying a function name like `spiflash_start_core` in a
linker fragment automatically includes input section names matching
`spiflash_start_core(\..*)?$`.
Signed-off-by: Frantisek Hrbata <frantisek.hrbata@espressif.com>
This commit is contained in:
@@ -320,6 +320,19 @@ class ObjectNode(EntityNode):
|
||||
if obj_sections:
|
||||
symbol = entity.symbol
|
||||
remove_sections = [s.replace('.*', '.%s' % symbol) for s in sections if '.*' in s]
|
||||
# As part of IPA optimization, the compiler may perform
|
||||
# constant propagation and generate specialized versions of a
|
||||
# function. For example, for the function spiflash_start_core,
|
||||
# the compiler might also generate a
|
||||
# spiflash_start_core.constprop.0 symbol, which will be placed
|
||||
# in a separate input section named
|
||||
# .text.spiflash_start_core.constprop.0. Ensure that such
|
||||
# generated functions are placed into the appropriate marker as
|
||||
# well.
|
||||
remove_sections_patterns = [s.replace('.*', f'.{symbol}.*') for s in sections if '.*' in s]
|
||||
for pattern in remove_sections_patterns:
|
||||
remove_sections.extend(fnmatch.filter(obj_sections, pattern))
|
||||
|
||||
filtered_sections = [s for s in obj_sections if s not in remove_sections]
|
||||
|
||||
if set(filtered_sections) != set(obj_sections):
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -33,6 +33,7 @@ FREERTOS = Entity('libfreertos.a')
|
||||
CROUTINE = Entity('libfreertos.a', 'croutine')
|
||||
TIMERS = Entity('libfreertos.a', 'timers')
|
||||
TEMPERATURE_SENSOR_PERIPH = Entity('libsoc.a', 'temperature_sensor_periph')
|
||||
ESP_FLASH_API = Entity('libspi_flash.a', 'esp_flash_api')
|
||||
|
||||
FREERTOS2 = Entity('libfreertos2.a')
|
||||
|
||||
@@ -72,6 +73,9 @@ class GenerationTest(unittest.TestCase):
|
||||
with open('data/libsoc.a.txt') as objdump:
|
||||
self.entities.add_sections_info(objdump)
|
||||
|
||||
with open('data/libspi_flash.a.txt') as objdump:
|
||||
self.entities.add_sections_info(objdump)
|
||||
|
||||
with open('data/linker_script.ld') as linker_script:
|
||||
self.linker_script_expect = LinkerScript(linker_script)
|
||||
|
||||
@@ -340,6 +344,73 @@ entries:
|
||||
|
||||
self.compare_rules(expected, actual)
|
||||
|
||||
def test_nondefault_mapping_symbol_with_suffix(self):
|
||||
# Test a mapping entry that differs from the default for a symbol
|
||||
# generated by the compiler, such as those created during IPA
|
||||
# optimization for constant propagation.
|
||||
#
|
||||
# There should be exclusions in the default commands for flash_text, as
|
||||
# well as the implicit intermediate object command with an exclusion
|
||||
# from default:
|
||||
#
|
||||
# flash_text
|
||||
# *((EXCLUDE_FILE(*libspi_flash.a:esp_flash_api.*)) .text ...) A
|
||||
# *libspi_flash.a:esp_flash_api.*(.text.check_chip_pointer_default ...) B
|
||||
#
|
||||
# Commands for placing the generated symbol in iram should be created,
|
||||
# and they must also include .text.spiflash_start_core.constprop.0,
|
||||
# even though the placement is specified only for the
|
||||
# spiflash_start_core symbol.
|
||||
#
|
||||
# iram0_text
|
||||
# *(.iram ...)
|
||||
# *libspi_flash.a:esp_flash_api.*(.literal.spiflash_start_core .text.spiflash_start_core
|
||||
# .text.spiflash_start_core.constprop.0) C
|
||||
mapping = """
|
||||
[mapping:test]
|
||||
archive: libspi_flash.a
|
||||
entries:
|
||||
esp_flash_api:spiflash_start_core (noflash) #1
|
||||
"""
|
||||
self.add_fragments(mapping)
|
||||
actual = self.generation.generate(self.entities, False)
|
||||
expected = self.generate_default_rules()
|
||||
|
||||
flash_text = expected['flash_text']
|
||||
iram0_text = expected['iram0_text']
|
||||
|
||||
# Generate exclusion in flash_text A
|
||||
flash_text[0].exclusions.add(ESP_FLASH_API)
|
||||
|
||||
# Generate intermediate command B
|
||||
# List all relevant sections except the symbol
|
||||
# being mapped
|
||||
esp_flash_api_sections = self.entities.get_sections('libspi_flash.a', 'esp_flash_api')
|
||||
filtered_sections = fnmatch.filter(esp_flash_api_sections, '.literal.*')
|
||||
filtered_sections.extend(fnmatch.filter(esp_flash_api_sections, '.text.*'))
|
||||
|
||||
filtered_sections = [s for s in filtered_sections if not s.endswith('spiflash_start_core.constprop.0')]
|
||||
filtered_sections.append('.text')
|
||||
|
||||
flash_text.append(InputSectionDesc(ESP_FLASH_API, set(filtered_sections), []))
|
||||
|
||||
# Input section commands in iram_text for #1 C
|
||||
iram0_text.append(
|
||||
InputSectionDesc(
|
||||
ESP_FLASH_API,
|
||||
set(
|
||||
[
|
||||
'.literal.spiflash_start_core',
|
||||
'.text.spiflash_start_core',
|
||||
'.text.spiflash_start_core.constprop.0',
|
||||
]
|
||||
),
|
||||
[],
|
||||
)
|
||||
)
|
||||
|
||||
self.compare_rules(expected, actual)
|
||||
|
||||
def test_nondefault_mapping_all_symbols(self):
|
||||
# Test mapping entry different from default for all .rodata.* symbols in the temperature_sensor_periph
|
||||
# object file. There should be exclusion in the default commands for flash_rodata, but
|
||||
|
||||
Reference in New Issue
Block a user