
Former RISC-V developers face a challenge: the code density of the RISC-V instruction set for deeply embedded processors lags behind existing ARM Cortex-M tools. However, many new products released by SEGGER are changing this situation, such as the recently released SEGGER Linker, which can reduce the code size of RISC-V by 15%. SEGGER’s Runtime Library is optimized for the performance and code size of RISC-V processors.
SEGGER’s linker supports the 32-bit RISC-V instruction sets RV32I and RV32E. It not only provides all the features of the Arm version linker but also adds new functionalities to fully utilize the RISC-V instruction set.
Optimization Effects
The table below shows the optimization effects of the SEGGER linker for RISC-V processors (test code attached later):

The GNU linker and library use SiFive’s GNU Embedded for RISC-V and SEGGER products respectively.
From the comparison results, the performance of the SEGGER linker is superior to that of GNU. Moreover, the SEGGER real-time runtime library performs even better on RISC-V! The combination of both (based on the Embedded Studio environment) reduces the Flash usage of the same application from 47784 bytes to 11270 bytes, a reduction of 76.4%. Even if only the SEGGER linker is used to relink the application, code space can still be saved by 13%.
Optimization Implementation
Relocation
The SEGGER linker focuses heavily on reducing code size. The compiler relocates separately compiled object module code to allow them to link together. However, the compiler does not know the final placement of functions and data objects (controlled by the linker script). Therefore, it makes worst-case assumptions about all function calls and accesses to global data (read/write and read-only data), resulting in relatively long code sequences.
The RISC-V architecture provides smaller and more compact instructions, but due to unknown addresses at compile time, the compiler cannot optimize the instructions, leaving it to the linker to optimize.
Smart Relaxation: Smaller and Faster Code
The job of the linker is to resolve these inter-module references determined by the compiler. A simple linker merely patches and completes references. A powerful linker will “smartly relax” the instruction sequences to smaller instruction sequences based on known resolved target addresses. A fully-featured linker will rearrange code and data, which requires careful consideration of segment layout to avoid causing problems.
The SEGGER linker uses carefully tuned heuristic methods to layout code and data along with the linker script, maximizing the applicable instances of “smart relaxation”.
For example, even if closely related groups of functions come from different object files, they can be placed closely together. This placement can reduce the function calls made by the compiler from two 32-bit instructions to one 32-bit instruction or even a more compact 16-bit instruction.
Benefits of Smart Relaxation for Data
Relaxation can also generate more compact data. We typically access global data using a 32-bit base register plus an offset. The GNU linker can gather “short” data items together, flexibly handling this situation using a global pointer, thereby reducing one instruction. However, it cannot automatically place the global pointer and requires the user to group data and manually assign the global pointer. With the powerful computers we have today, why not let the computer find the best data layout and location for the global pointer? That is what the SEGGER linker does, though you can still manually group segments and set global pointers based on your own choices.
Is having two or three global pointers better?
The RISC-V register file has a dedicated thread pointer register for local thread data (tp). If the application does not use local data, tp will not be used (you can have a multitasking application, but if the application does not use a copy of data for each thread, then no thread pointer is needed). The SEGGER linker uses this register as a second global variable base address. All transformations applied to the global pointer can also be applied to the thread pointer. Of course, you can specify models for the thread pointer you want to use:
Retain; use as global base; for local thread data; automatically allocate its model based on input files.Moreover if you link the application at zero location, the SEGGER linker will automatically convert the code to use the “zero” register x0 for all optimizations.Thus, now the SEGGER linker can handle code and data, and layout them using three register-based pointers for optimal usage.
Gains
The optimizations and transformations provided by Segger have dual benefits: the resulting code is not only smaller but also faster!
A slightly lower performance optimization (branch loss) is springboarding. The SEGGER linker can convert call groups and reduce single-branch runtime through common springboard jumps, greatly shrinking code. This is another tool you can use, accepting some extra cycles to make the application fit into Flash.
The user has full control: while all transformations can be applied to the entire program, they can be enabled or disabled individually. In the near future, we will also add segment control-based optimization methods, which will be very suitable for isolating time-critical or space-critical functionalities.
Summary
The SEGGER linker brings many benefits to existing applications. Upholding SEGGER’s design philosophy, the SEGGER linker works simply: to ensure your application performs optimally, allowing you to rely on it to automate the build process. If you prefer manual control, the SEGGER linker accommodates that as well, enabling you to precisely control the placement of code and data, where base addresses point, and how code transformations are made.
Appendix – Benchmark Code
This article tests data using the benchmark application.
/*****************************************************************
* (c) SEGGER Microcontroller GmbH *
* The Embedded Experts *
* www.SEGGER.com *
******************************************************************
————————– END-OF-HEADER ————————
File : bench-fp-math-size.c
Purpose : Benchmark overall size of FP library.
*/
/*****************************************************************
*
* #include section
*
******************************************************************
*/
#include <math.h>
/*****************************************************************
*
* Static data
*
******************************************************************
*/
static volatile float vf;
static volatile double vd;
/*****************************************************************
*
* Public code
*
******************************************************************
*/
int main(void) {
float f;
double d;
//
f = vf;
f = sinf(f);
f = cosf(f);
f = tanf(f);
f = asinf(f);
f = acosf(f);
f = atanf(f);
f = sinhf(f);
f = coshf(f);
f = tanhf(f);
f = asinhf(f);
f = acoshf(f);
f = atanhf(f);
vf = f;
//
d = vd;
d = sin(d);
d = cos(d);
d = tan(d);
d = asin(d);
d = acos(d);
d = atan(d);
d = sinh(d);
d = cosh(d);
d = tanh(d);
d = asinh(d);
d = acosh(d);
d = atanh(d);
vd = d;
//
return 0;
}
/*************************** End of file ***********************/
Welcome to follow our WeChat public account 【麦克泰技术】, reply “Join Group” to join the technical exchange group as prompted
Product Consultation:
Beijing: 010-62975900
Shanghai: 021-62127690
Shenzhen: 0755-82977971
