# Preprocessing: include, define and selection Layer 1, the common language. Verified against the preprocessor inside `go tool asm` of Go 1.27.1 (`cmd/asm/internal/lex`), whose directives are `#define`, `#undef`, `#include`, `#ifdef`, `#ifndef`, `#else`, `#endif` and `#line`, and against gasm's implementation, which is differentially tested against the toolchain's. Input runs through a simplified C preprocessor before the parser sees it. The set is deliberately small: there is no `#if` with constant expressions and no token pasting with `##`. `#line` is honoured, so it changes the positions the assembler reports and records. ## #include ```text #include "textflag.h" #include "go_asm.h" #include "defs_linux_amd64.h" ``` The search path, in order: the directory of the including file, then the directories given by repeatable `-I` flags. The assembler seeds no default of its own: a bare `go tool asm` invocation finds none of the standard headers, and it is the `go` build system that passes `$GOROOT/pkg/include` among the `-I` directories when it drives the build. That directory ships `textflag.h`, `funcdata.h` and the per architecture register headers. Includes nest; a file included twice through different paths is processed twice, which is why headers guard their defines. ## #define and #undef ```text #define bufSize 1024 #define MOVD(d, s) MOVQ s, d #undef bufSize ``` - An object macro replaces its name with its token sequence at the point of use. - A parameterised macro takes its arguments in parentheses and substitutes them into the body. Macro parameters compose with the rest of the language: an argument used with an element suffix, as in `A.S4` on the vector forms, substitutes correctly. - Redefinition is an error; `#undef` first, or pick a new name. - The `-D name[=value]` flag predefines an object macro from the command line, repeatable, exactly as `#define` would; a `-D` without a value defines the name as `1`. - Expansion happens when the name is used, so a macro may expand to instructions, operands or fragments of either, and a macro body may use macros defined before it. `textflag.h` and `funcdata.h` are themselves ordinary `#define` files: the flag names and the runtime macros are preprocessor definitions, not language keywords. That is why a missing include produces a parser error at the first use of `NOSPLIT` rather than a complaint about the name. ## #ifdef, #ifndef, #else, #endif ```text #ifdef GOOS_windows #define SYSCALL_INT 0x2b #endif ``` Selection is by defined-name only: `#ifdef`, `#ifndef`, `#else`, `#endif`, nesting freely. There is no `#if defined(x) && y`, because the preprocessor evaluates no expressions; reach that with a build-tag Go file generating a header, which is exactly how the runtime's own `go_asm.h` and defs headers are produced. ## What preprocessing does not cover The preprocessor is textual and runs first, so it knows nothing of assembly semantics: it does not check that a macro expansion is a legal instruction, and it does not participate in the constant expression evaluator, which runs later, in the parser. A constant folded with `#define` and a constant folded in an operand expression end at the same value through different doors; GOOBJ.md records both in the object identically.