80 lines
3.2 KiB
Markdown
80 lines
3.2 KiB
Markdown
# 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.
|