Instruction Pattern Matching: The Macros and Functions Behind INSTPAT

Engineering

Posted by Bruce Lee on 2024-04-08

About Me

Welcome to my blog! This is where I collect my observations and notes on programming and technology. The main subjects range from implementation details to broader ideas about programming.

Main Topics

  • Engineering Projects: Exploring implementation details and how technical systems work.
  • C/C++: Notes on language features and programming techniques.
  • The Programmer’s Perspective: Ideas about developing a career and a way of thinking as a programmer.

For more, visit the categories page.

Contact

If you have questions or would like to discuss something, please get in touch through the About page.

Thank you for reading and for your support. I hope these notes help you on your own technical journey!


How Does INSTPAT_INST Work?

The macro accesses s, a Decode object, and follows its isa field to the inst union and its val member. This is the binary encoding of the current instruction.

INSTPAT uses that value after calling pattern_decode to determine whether the current instruction matches the pattern under consideration.

How Does INSTPAT_MATCH Work?

This macro uses variable arguments. It calls decode_operand to decode the operands for the selected instruction format, then expands the supplied instruction body.

INSTPAT invokes it only after the pattern comparison succeeds. At that point, the format has been selected and the instruction’s operation can be executed. This is the innermost part of the matching-and-execution sequence.

What Does INSTPAT_START Do?

At the most visible level, it marks the beginning of a group of instruction patterns. It also establishes the address of the label used to leave the group after a successful match.

How Does INSTPAT_START Do That?

The macro relies on concat, whose token-pasting mechanism is explained in C Preprocessor Token-Pasting Operator.

The __instpat_end variable stores the destination needed by the computed jump. At the end of the successful-match block in INSTPAT, the code uses:

1
goto *(__instpat_end); \

This jumps to the generated end label. The implementation uses a GNU C extension for label addresses and computed goto; it is not an ordinary function-pointer call.

What Does INSTPAT_END Do?

It wraps concat to generate the corresponding label name and appends a colon, defining the jump destination.

The purpose is to stop checking the remaining patterns once an instruction has matched. Jumping out immediately avoids redundant comparisons.

How Is the End Label Constructed?

It uses the same token-pasting principle as concat. Both INSTPAT_START and INSTPAT_END accept a naming argument, but the calls in decode_exec in inst.c leave it empty. The parameter leaves room for separately named pattern groups.

A Closer Look at INSTPAT

inst.c invokes INSTPAT repeatedly for the supported instructions. The current instruction is compared with these patterns in their listed order inside decode_exec.

Each invocation declares key, mask, and shift. In the patterns I had examined, I had not yet encountered a nonzero shift; the mechanism supports cases beyond those examples.

The variadic macro receives the pattern string, instruction name, instruction format, and operation body. It calls pattern_decode with the pattern string and its length, plus the addresses of key, mask, and shift.

After those values have been prepared, the if condition shifts the current instruction encoding, applies the mask, and compares the result with the key. If the comparison succeeds, INSTPAT_MATCH decodes the operands and performs the operation. The computed jump then transfers control to the end label described above.

What Is pattern_decode For?

Defined in include/cpu/decode.h, pattern_decode prepares the key, mask, and shift used by the subsequent INSTPAT comparison. It converts a textual pattern into values suitable for checking an instruction encoding.

It is declared inline. That allows the compiler to inline its implementation, but inline functions are not expanded by the preprocessor in the same way as macros.

The function takes the pattern string and its length, together with pointers to the output values. Internally, helper macros process the individual pattern characters.

What Does macro(i) Do?

The helper distinguishes fixed bits from don’t-care positions. Fixed positions contribute to the key and are enabled in the mask; irrelevant positions are masked out. shift supports the treatment of trailing don’t-care positions.

The implementation invokes macro64(0), using recursively composed helper macros to expand a sequence of character-processing operations. This unrolls processing of up to 64 pattern characters; it does not by itself mean that every instruction being decoded is 64 bits long.

After the pattern has been processed, control returns to the comparison in INSTPAT.

Returning to INSTPAT_MATCH

The macro defined in src/isa/riscv32/inst.c calls decode_operand and then executes the operation supplied through the variable arguments:

1
static void decode_operand(Decode *s, int *rd, word_t *src1, word_t *src2, word_t *imm, int type)

The operand-decoding discussion follows decode_operand in more detail. The instruction’s actual computation is provided through __VA_ARGS__. For that technique, see Variadic Macros, Token Pasting, and Callback-Style Macro Expansion.


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. All the images used in the blog are my original works or AI works, if you want to take it,don't hesitate. Thank you !