Unused strings Optimization

Asked by Rahul Gundecha

Hi all,

This problem is already discussed on several other forums like below:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54303

To summarize, the strings from unused function gets pulled in the executable. Note that the function itself is not pulled in.
Is there some way to tackle this issue?

Here are the gcc options used:
-g -Os -fdata-sections -ffunction-sections -ffreestanding -MMD -Wall -fno-strict-aliasing -fno-common -fno-merge-constants
-T <ld script> -nostartfiles -Xlinker -M -Xlinker -Map=<map file> -Xlinker --cref -Xlinker --gc-sections

As per gcc man page, -fmerge-constants is enabled when -Os is used. So does it make sense to use -fno-merge-constants along with -Os?

Thanks for your help!

Thanks,
Rahul

Question information

Language:
English Edit question
Status:
Answered
For:
GNU Arm Embedded Toolchain Edit question
Assignee:
No assignee Edit question
Last query:
Last reply:
Revision history for this message
Thomas Preud'homme (thomas-preudhomme) said :
#1

Hi Rahul,

For -fno-merge-constants to be worthwhile the number of strings duplicated would need to be more than the number of strings that can be removed by --gc-sections which the compiler cannot know in advance. Even if this is generally true, that would require the linker to understand which string(s) a given function use which is not trivial. Consider the following code:

#include <string.h>

int
compare_with_foo (char *s)
{
  return strcmp (s, "foo");
}

int
compare_with_bar (char *s)
{
  return strcmp (s, "bar");
}

When compiled with arm-none-eabi-gcc -S -Os -fdata-sections -ffunction-sections -fno-common -fno-merge-constants it gives the following sequence for compare_with_bar:

compare_with_bar:
        push {r4, lr}
        ldr r1, .L5
        @ sp needed
        adds r1, r1, #4
        bl strcmp
        pop {r4, pc}

This would require the linker to understand that ldr + adds points to the bar string. This is too much to ask of the linker and since overall -merge-constants is likely to give size savings, the current behavior of -Os to enable this switch makes sense.

Best regards.

Revision history for this message
Rahul Gundecha (rahul-gundecha) said :
#2

Hi Thomas,

Thanks for the detailed response.
However I see that the string from unused function is un-necessarily getting pulled in, which means may be "-fno-merge-constants" do not work when "-Os" is passed.

Please conside following code:
#include <stdio.h>

int my_function()
{
 printf("This string should be pulled in!");
 return 0;
}

int unused_function()
{
 printf("This is unwanted string!");
 return 0;
}

int main()
{
 return my_function();
}

arm-none-eabi-gcc -S -Os -ffunction-sections -fno-common -fno-merge-constants -o unused-strings.o unused-strings.c

 .cpu arm7tdmi
 .fpu softvfp
 .eabi_attribute 20, 1
 .eabi_attribute 21, 1
 .eabi_attribute 23, 3
 .eabi_attribute 24, 1
 .eabi_attribute 25, 1
 .eabi_attribute 26, 1
 .eabi_attribute 30, 4
 .eabi_attribute 34, 0
 .eabi_attribute 18, 4
 .file "unused-strings.c"
 .section .text.my_function,"ax",%progbits
 .align 2
 .global my_function
 .type my_function, %function
my_function:
 @ Function supports interworking.
 @ args = 0, pretend = 0, frame = 0
 @ frame_needed = 0, uses_anonymous_args = 0
 stmfd sp!, {r3, lr}
 ldr r0, .L3
 bl printf
 mov r0, #0
 ldmfd sp!, {r3, lr}
 bx lr
.L4:
 .align 2
.L3:
 .word .LANCHOR0
 .size my_function, .-my_function
 .section .text.unused_function,"ax",%progbits
 .align 2
 .global unused_function
 .type unused_function, %function
unused_function:
 @ Function supports interworking.
 @ args = 0, pretend = 0, frame = 0
 @ frame_needed = 0, uses_anonymous_args = 0
 stmfd sp!, {r3, lr}
 ldr r0, .L7
 bl printf
 mov r0, #0
 ldmfd sp!, {r3, lr}
 bx lr
.L8:
 .align 2
.L7:
 .word .LANCHOR0+33
 .size unused_function, .-unused_function
 .section .text.startup.main,"ax",%progbits
 .align 2
 .global main
 .type main, %function
main:
 @ Function supports interworking.
 @ args = 0, pretend = 0, frame = 0
 @ frame_needed = 0, uses_anonymous_args = 0
 stmfd sp!, {r3, lr}
 ldr r0, .L11
 bl printf
 mov r0, #0
 ldmfd sp!, {r3, lr}
 bx lr
.L12:
 .align 2
.L11:
 .word .LANCHOR0
 .size main, .-main
 .section .rodata
 .set .LANCHOR0,. + 0
.LC0:
 .ascii "This string should be pulled in!\000"
.LC1:
 .ascii "This is unwanted string!\000"
 .ident "GCC: (GNU Tools for ARM Embedded Processors) 4.9.3 20141119 (release) [ARM/embedded-4_9-branch revision 218278]"

Got same result when compiled with gcc
$ gcc -Wall -fdata-sections -ffunction-sections -c unused-strings.c -o unused-strings.o
$ gcc -fno-common -fno-merge-constants -Xlinker --gc-sections -o unused-strings unused-strings.o
$ strings unused-strings | grep string
This string should be pulled in!
This is unwanted string!

Thanks,
Rahul

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) said :
#3

Hi Rahul,

This is because of the same second reason I gave in my previous post: there is no easy way for the linker to find the strings that a function uses. I'm afraid if you want to achieve this you will have to do some conditional compilation using preprocessor directives.

Best regards.

Revision history for this message
Andreas Fritiofson (andreas-fritiofson) said :
#4

Thomas: I don't think the linker can be blamed or needs to be involved, it's an issue with where the compiler stores the string constants. If the compiler had placed the string constant in the same section as the function or in a section of its own, the unused string would really have been discarded by --gc-sections. Instead the compiler merges all string constants into the .rodata section so the linker has no chance of discarding individual strings.

I assume the OP had hoped that -fno-merge-constants would somehow alter this behaviour and keep string constants separate but is doesn't seem it has that effect.

I can add that with LTO the necessary information is actually available a link time. Indeed it seems it also has effect:

$ gcc -Os -Wall -flto -c unused-strings.c -o unused-strings.o
$ gcc -flto -o unused-strings unused-strings.o
$ strings unused-strings | grep string
This string should be pulled in!

Can you help with this problem?

Provide an answer of your own, or ask Rahul Gundecha for more information if necessary.

To post a message you must log in.