How to Add Built-in Macros to riscv-gcc

The riscv-gcc tool includes some built-in macro parameters. We can use these built-in macros to determine the behavior of the compiler.

1. Viewing GCC Built-in Macro Parameters

Here, we take the riscv-nuclei-elf-gcc toolchain released by Chipone Technology as an example.

Using the following command, we can get the built-in macro parameters of this tool:

riscv-nuclei-elf-gcc -E -dM a.h | grep riscv

We can obtain the following macro parameters:

#define __riscv 1

#define __riscv_atomic 1

#define __riscv_cmodel_medlow 1

#define __riscv_fdiv 1

#define __riscv_float_abi_double 1

#define __riscv_flen 64

#define __riscv_compressed 1

#define __riscv_mul 1

#define __riscv_muldiv 1

#define __riscv_xlen 32

#define __riscv_fsqrt 1

#define __riscv_div 1

From the built-in macro parameters, we can see that the compiler supports the RV64IMAFDC instruction set architecture by default.

If we use the following command:

riscv-nuclei-elf-gcc -march=rv32gc -mabi=ilp32 -E -dM a.h | grep riscv

We get the following results:

#define __riscv 1

#define __riscv_atomic 1

#define __riscv_cmodel_medlow 1

#define __riscv_float_abi_soft 1

#define __riscv_fdiv 1

#define __riscv_flen 64

#define __riscv_compressed 1

#define __riscv_mul 1

#define __riscv_muldiv 1

#define __riscv_xlen 32

#define __riscv_fsqrt 1

#define __riscv_div 1

From the built-in macro parameters, we can see that the compiler supports the RV32IMAFDC instruction set architecture.

2. Adding Built-in Macro Parameters

For riscv, it supports the p extension, which is aimed at DSP applications.

If we want the compiler to define the __riscv_dsp macro when the -march option specifies the p extension instruction set, we can do so. If the p extension instruction set is not specified, then the __riscv_dsp macro will not be defined.

In this way, the DSP program we write can use this macro to determine whether the p extension instruction set is supported.

Below is a brief description of how to implement this feature, that is, how to add built-in macro parameters based on the passed instruction set architecture parameters.

Here, we mainly refer to the following commit of riscv-gcc:

https://github.com/riscv/riscv-gcc/commit/06ab742f982d23488ec2d8c0266cb720fe775f7c

This commit adds support for RV32E to riscv-gcc.

3. Modifying riscv.opt

First, modify the gcc/config/riscv/riscv.opt file to add the DSP macro.

How to Add Built-in Macros to riscv-gcc

The gcc script tool will process this file, expanding macros through masks to define new macro parameters.

  • MASK_DSP

  • TARGET_DSP

In the gcc/options.h file under the compilation directory, there are definitions for these macro parameters.

Definitions of TARGET and MASK macros:

#define MASK_DIV (1U << 0)

#define MASK_EXPLICIT_RELOCS (1U << 1)

#define MASK_FDIV (1U << 2)

#define MASK_SAVE_RESTORE (1U << 3)

#define MASK_STRICT_ALIGN (1U << 4)

#define MASK_64BIT (1U << 5)

#define MASK_ATOMIC (1U << 6)

#define MASK_DOUBLE_FLOAT (1U << 7)

#define MASK_DSP (1U << 8)

#define MASK_HARD_FLOAT (1U << 9)

#define MASK_MUL (1U << 10)

#define MASK_RVC (1U << 11)

#define MASK_RVE (1U << 12)

#define TARGET_DIV ((target_flags & MASK_DIV) != 0)

#define TARGET_DIV_P(target_flags) (((target_flags) & MASK_DIV) != 0)

#define TARGET_EXPLICIT_RELOCS ((target_flags & MASK_EXPLICIT_RELOCS) != 0)

#define TARGET_EXPLICIT_RELOCS_P(target_flags) (((target_flags) & MASK_EXPLICIT_RELOCS) != 0)

#define TARGET_FDIV ((target_flags & MASK_FDIV) != 0)

#define TARGET_FDIV_P(target_flags) (((target_flags) & MASK_FDIV) != 0)

#define TARGET_SAVE_RESTORE ((target_flags & MASK_SAVE_RESTORE) != 0)

#define TARGET_SAVE_RESTORE_P(target_flags) (((target_flags) & MASK_SAVE_RESTORE) != 0)

#define TARGET_STRICT_ALIGN ((target_flags & MASK_STRICT_ALIGN) != 0)

#define TARGET_STRICT_ALIGN_P(target_flags) (((target_flags) & MASK_STRICT_ALIGN) != 0)

#define TARGET_64BIT ((target_flags & MASK_64BIT) != 0)

#define TARGET_ATOMIC ((target_flags & MASK_ATOMIC) != 0)

#define TARGET_DOUBLE_FLOAT ((target_flags & MASK_DOUBLE_FLOAT) != 0)

#define TARGET_DSP ((target_flags & MASK_DSP) != 0)

#define TARGET_HARD_FLOAT ((target_flags & MASK_HARD_FLOAT) != 0)

#define TARGET_MUL ((target_flags & MASK_MUL) != 0)

#define TARGET_RVC ((target_flags & MASK_RVC) != 0)

#define TARGET_RVE ((target_flags & MASK_RVE) != 0)

The options.h file is generated by gcc’s built-in scripts. I haven’t yet fully understood how this script works.

4. Modifying riscv-common.c

After obtaining the above macros, we need to modify the gcc/common/config/riscv/riscv-common.c file.

In the riscv_parse_arch_string function, it will analyze the passed –march parameter and then set the flags by adding the corresponding TARGET support to the flags.

By imitating the support for the c extension, we can add support for the p extension.

static void

riscv_parse_arch_string (const char *isa, int *flags, location_t loc)

{

riscv_subset_list *subset_list;

subset_list = riscv_subset_list::parse (isa, loc);

if (!subset_list)

return;

if (subset_list->xlen () == 32)

*flags &= ~MASK_64BIT;

else if (subset_list->xlen () == 64)

*flags |= MASK_64BIT;

*flags &= ~MASK_RVE;

if (subset_list->lookup (“e”))

*flags |= MASK_RVE;

*flags &= ~MASK_MUL;

if (subset_list->lookup (“m”))

*flags |= MASK_MUL;

*flags &= ~MASK_ATOMIC;

if (subset_list->lookup (“a”))

*flags |= MASK_ATOMIC;

*flags &= ~(MASK_HARD_FLOAT | MASK_DOUBLE_FLOAT);

if (subset_list->lookup (“f”))

*flags |= MASK_HARD_FLOAT;

if (subset_list->lookup (“d”))

*flags |= MASK_DOUBLE_FLOAT;

*flags &= ~MASK_RVC;

if (subset_list->lookup (“c”))

*flags |= MASK_RVC;

*flags &= ~MASK_DSP;

if (subset_list->lookup (“p”))

*flags |= MASK_DSP;

if (current_subset_list)

delete current_subset_list;

current_subset_list = subset_list;

}

By calling the lookup function of the subset_list, we can check if the corresponding instruction set architecture has been passed in. If it has, we set the corresponding MASK in the flags.

5. Modifying riscv-c.c

Modify the gcc/config/riscv/riscv-c.c file to add built-in macros.

The riscv_cpu_cpp_builtins function in this file is the function that adds built-in macro parameters.

void

riscv_cpu_cpp_builtins (cpp_reader *pfile)

{

builtin_define (“__riscv”);

if (TARGET_RVC)

builtin_define (“__riscv_compressed”);

if (TARGET_RVE)

builtin_define (“__riscv_32e”);

if (TARGET_ATOMIC)

builtin_define (“__riscv_atomic”);

if (TARGET_MUL)

builtin_define (“__riscv_mul”);

if (TARGET_DIV)

builtin_define (“__riscv_div”);

if (TARGET_DIV && TARGET_MUL)

builtin_define (“__riscv_muldiv”);

if (TARGET_DSP)

builtin_define (“__riscv_dsp”);

builtin_define_with_int_value (“__riscv_xlen”, UNITS_PER_WORD * 8);

if (TARGET_HARD_FLOAT)

builtin_define_with_int_value (“__riscv_flen”, UNITS_PER_FP_REG * 8);

if (TARGET_HARD_FLOAT && TARGET_FDIV)

{

builtin_define (“__riscv_fdiv”);

builtin_define (“__riscv_fsqrt”);

}

switch (riscv_abi)

{

case ABI_ILP32E:

builtin_define (“__riscv_abi_rve”);

gcc_fallthrough ();

case ABI_ILP32:

case ABI_LP64:

builtin_define (“__riscv_float_abi_soft”);

break;

case ABI_ILP32F:

case ABI_LP64F:

builtin_define (“__riscv_float_abi_single”);

break;

case ABI_ILP32D:

case ABI_LP64D:

builtin_define (“__riscv_float_abi_double”);

break;

}

switch (riscv_cmodel)

{

case CM_MEDLOW:

builtin_define (“__riscv_cmodel_medlow”);

break;

case CM_MEDANY:

builtin_define (“__riscv_cmodel_medany”);

break;

case CM_PIC:

builtin_define (“__riscv_cmodel_pic”);

break;

}

}

We need to add our code here:

if (TARGET_DSP)

builtin_define (“__riscv_dsp”);

Since the flags have already set MASK_DSP, the TARGET_DSP macro is defined as follows:

#define TARGET_DSP ((target_flags & MASK_DSP) != 0)

If the return value of this macro is not 0, it indicates that the -march has passed in the p extension instruction set, so the builtin_define function will be called to define the __riscv_dsp macro.

If the return value of this macro is 0, it indicates that the -march has not passed in the p extension instruction set, so the builtin_define function will not be called to define the __riscv_dsp macro.

6. Testing

Recompile the riscv-gcc toolchain and then perform testing:

Use the following command:

riscv-nuclei-elf-gcc -march=rv64gcp -mabi=lp64 -E -dM a.h | grep riscv

We get:

#define __riscv 1

#define __riscv_atomic 1

#define __riscv_cmodel_medlow 1

#define __riscv_float_abi_soft 1

#define __riscv_fdiv 1

#define __riscv_flen 64

#define __riscv_compressed 1

#define __riscv_mul 1

#define __riscv_muldiv 1

#define __riscv_xlen 64

#define __riscv_fsqrt 1

#define __riscv_div 1

#define __riscv_dsp 1

From the results, we can see the __riscv_dsp macro.

Using the following command:

riscv-nuclei-elf-gcc -march=rv64gc -mabi=lp64 -E -dM a.h | grep riscv

We get:

#define __riscv 1

#define __riscv_atomic 1

#define __riscv_cmodel_medlow 1

#define __riscv_float_abi_soft 1

#define __riscv_fdiv 1

#define __riscv_flen 64

#define __riscv_compressed 1

#define __riscv_mul 1

#define __riscv_muldiv 1

#define __riscv_xlen 64

#define __riscv_fsqrt 1

#define __riscv_div 1

From the results, we can see that there is no __riscv_dsp macro.

7. Conclusion

We have completed the addition of built-in macros to riscv-gcc.

If you simply want to add a macro that is not related to any parameters, you can directly modify the riscv-common.c file and call the builtin_define function to define the built-in macro.

If the macro you want to add is related to the -march parameter, you can follow the above process to modify and add built-in macros.

How to Add Built-in Macros to riscv-gcc

How to Add Built-in Macros to riscv-gcc

How to Add Built-in Macros to riscv-gcc

Leave a Comment