1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! `__builtin_expect` and `__builtin_expect_with_probability`, which are their first argument.
//!
//! Design: `spec/13-gnu-compat.md` section 13.5.
//!
//! These two say which way a branch is expected to go. The value of `__builtin_expect(x, c)` is
//! `x`, and everything after the first argument is a hint about how often that value will turn out
//! to be `c`. So the answer is the first argument, and the hint is dropped here because there is
//! nothing yet that could read it: branch weights arrive with the optimizer, and until then a node
//! carrying one would be a node every pass has to step over for no gain. `Opcode::Expect` is in the
//! IR waiting for that day.
//!
//! # Why this is not a link error, which is what it was
//!
//! A builtin nothing lowers reaches the assembler as a call to a name no object file defines, and
//! this is the one where that matters most. glibc's `<stdio.h>` writes `getc_unlocked` and its
//! neighbours as extern inline functions in terms of `__builtin_expect` as soon as `__OPTIMIZE__`
//! is set, so before this every program that included that header, called one of those functions
//! and asked for `-O1` failed to link on a name it never wrote. The wider defect, which is that any
//! unimplemented builtin does this rather than saying so, is tamnd/rucc#303.
//!
//! # Why the answer is taken after the call is checked and not before
//!
//! The families next door are recognised before the callee is looked up, because their type comes
//! out of the call and there is no prototype to check them against. These two have one:
//! `long(long, long)` in `features.toml`, which is what gcc gives them, and it is worth keeping.
//! It is where the argument count message comes from, it is what converts the first argument to
//! `long` so that `sizeof(__builtin_expect((char)1, 1))` is eight the way gcc has it, and it is
//! what reports a structure handed to the first parameter in the ordinary words. All of that would
//! have to be written again here to gain nothing.
//!
//! So the call is checked the whole ordinary way and the node is replaced at the end of it.
//!
//! # What happens to a side effect in the hint
//!
//! Whether the hint runs depends on the first argument, which is not a rule anybody would design
//! and is what gcc 16.2.0 does. A first argument that is a constant folds the whole call where it
//! is written and the hint goes with it, so `__builtin_expect(5, side())` never calls `side`. A
//! first argument that is not a constant leaves a call standing until well after the arguments
//! have been evaluated, so `__builtin_expect(x, side())` does call it.
//!
//! That was measured across five shapes at three optimization levels rather than reasoned about:
//! the hint dropped and the hint kept, the value used and the value thrown away, and a constant
//! first argument against a variable one. gcc gives the same answer at `-O0`, `-O1` and `-O2`.
//! This compiler agreed on the first two shapes and dropped the hint on the other three, which is
//! tamnd/rucc#584, and `execute/pr85156.c` in the GCC torture suite is a program that notices:
//! the value it returns is a `z++` written inside a hint.
//!
//! It is matched rather than tidied up because the whole reason a builtin exists is that a program
//! written against gcc gets gcc's answer, and there is no reading of this one that both keeps the
//! side effect and folds `sizeof(__builtin_expect((char)1, 1))` to eight.
use Symbol;
use Span;
use crateChecker;
use crate;
/// The names whose value is their first argument.
///
/// Both are rows of `features.toml` with a signature, which is what makes them ordinary calls up
/// to the point this replaces them, and the test at the bottom of this file is what keeps the two
/// lists from drifting apart.
const FAMILY: & = &;