Pages

Tuesday, August 4, 2026

RisvV C Compiler Dev Journal - 20260804

72 Test Cases, typedef, and Function Pointers

Today I reached another milestone on my RISC-V C compiler project with Rust. The regression suite has now grown to 72 executable test cases, and this milestone adds support for two important C language features: typedef and function pointers.

I've been following a simple rule throughout this project: every new language feature must come with at least one regression test. This gives me confidence to continue improving the compiler without worrying about breaking functionality that was implemented earlier.

The compiler has gradually evolved from handling simple expressions into supporting a substantial subset of the C language. So far it includes:

  • variables and arithmetic expressions

  • if, while, and for

  • functions and recursion

  • pointers and pointer arithmetic

  • arrays

  • structures

  • union

  • enum

  • typedef

  • function pointers

Adding typedef

Although typedef does not introduce a new type, it is an important C language feature that allows programmers to create aliases for existing types.

Supporting typedef required extending the compiler's symbol table and type system so that type aliases could be correctly distinguished from ordinary identifiers and resolved during semantic analysis.

With typedef in place, complex declarations become much easier to read. This is especially important for function pointers, where the original C syntax can quickly become difficult to understand.

For example:

typedef int (*BinOp)(int, int);

is much cleaner than repeatedly writing the full function pointer declaration throughout a program.

Why Function Pointers?

Function pointers are one of the features that make C both flexible and powerful. They enable callbacks, dispatch tables, state machines, and many programming patterns commonly found in embedded software, operating systems, and low-level libraries.

Supporting function pointers required more than just parsing a complicated declaration. The compiler now needs to correctly handle:

  • function pointer type resolution

  • assignments between functions and function pointer variables

  • indirect function calls

  • passing function pointers as parameters

  • returning function pointers from functions

  • function pointers stored inside arrays and structures

Test Case #72

To validate both typedef and function pointer support, I created a regression test that combines several real-world usage patterns into a single program.

The test covers:

  • a typedef for a function pointer type

  • multiple arithmetic callback functions

  • an array of function pointers

  • passing function pointers as parameters

  • returning a function pointer from another function

  • storing function pointers inside a structure

  • indirect function calls through variables, arrays, and structure members

The test program defines several operations (add, sub, mul, and negate_via) and exercises different ways of invoking them through function pointers.

The expected final result is 42, which becomes the regression check for the entire feature set.

This type of test is especially valuable because it validates the interaction between multiple compiler components:

  • parsing

  • type resolution

  • semantic analysis

  • function call generation

  • memory layout handling

  • RISC-V code generation

A More Stable Foundation

One thing I've noticed during recent development is that segmentation faults have become much less common.

Earlier in the project, adding a new language feature often exposed issues deep inside the compiler. These problems usually came from assumptions that were valid when the compiler was smaller but became incorrect as more language features were introduced.

Recently, the development process has changed. Most of my effort is now spent adding new capabilities and expanding test coverage rather than debugging compiler crashes.

I believe this is a sign that the compiler framework itself is becoming more stable. The internal architecture, type system, and regression framework are mature enough to support continued growth.

There are still many bugs to discover, but it is encouraging to see the project moving from "building the foundation" toward "expanding capabilities."

Growing the Regression Suite

The compiler now includes 72 executable regression tests, and this number has become more meaningful than simply counting implemented features.

Every bug I fix becomes a permanent regression test.

Every new language feature adds another executable example.

Over time, the regression suite is becoming a living specification of what the compiler supports.

This approach also makes future optimization work safer because I can quickly verify that improvements do not break existing functionality.

What's Next?

With many core C language features now implemented—including struct, union, enum, typedef, and function pointers—the next phase of development will focus on improving compiler maturity in two areas.

The first area is expanding floating-point support. The compiler already has basic floating-point functionality, and the next step is to add more comprehensive regression tests covering more complex scenarios, including floating-point expressions, conversions, function calls, and interactions between integer and floating-point types.

The goal is not only to make floating-point programs compile, but also to build confidence that the generated RISC-V code behaves correctly across a wider range of real-world cases.

The second area is optimization. Up to this point, the primary focus has been correctness, language coverage, and compiler stability. With the framework becoming more mature and the regression suite growing, the next challenge is improving the quality of generated code.

Future optimization work will explore areas such as:

  • improving generated instruction sequences

  • reducing unnecessary operations

  • peephole optimization

  • intermediate representation improvements

  • exploring more advanced compiler optimization techniques

This marks an important transition for the project: moving from:

"Can the compiler correctly compile C programs?"

toward:

"Can the compiler generate efficient RISC-V code?"

I'm looking forward to this next stage of the journey.