rubycc 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +56 -0
- data/LICENSE.txt +21 -0
- data/NOTICE +52 -0
- data/README.md +208 -0
- data/data/README.md +117 -0
- data/data/r10_corpus_scan.json +4082 -0
- data/data/r10_manual_classification.json +5342 -0
- data/data/r10_verification_m4a.json +1531 -0
- data/data/r10_verification_m4b.json +1818 -0
- data/data/r10_verification_m4c.json +1489 -0
- data/data/r10_verification_m4d.json +318 -0
- data/data/r10_verification_rbs.json +192 -0
- data/data/verified_gems.json +397 -0
- data/exe/rmake +16 -0
- data/exe/rubycc +9 -0
- data/exe/rubycc-ar +114 -0
- data/exe/rubycc-doctor +14 -0
- data/exe/rubycc-pkgconf +8 -0
- data/include/float.h +87 -0
- data/include/iso646.h +18 -0
- data/include/libc/alloca.h +18 -0
- data/include/libc/arpa/inet.h +61 -0
- data/include/libc/assert.h +43 -0
- data/include/libc/dirent.h +72 -0
- data/include/libc/dlfcn.h +58 -0
- data/include/libc/features.h +205 -0
- data/include/libc/glibc/aarch64/ctype.h +108 -0
- data/include/libc/glibc/aarch64/endian.h +54 -0
- data/include/libc/glibc/aarch64/errno.h +154 -0
- data/include/libc/glibc/aarch64/fcntl.h +144 -0
- data/include/libc/glibc/aarch64/inttypes.h +182 -0
- data/include/libc/glibc/aarch64/limits.h +63 -0
- data/include/libc/glibc/aarch64/pthread.h +131 -0
- data/include/libc/glibc/aarch64/setjmp.h +84 -0
- data/include/libc/glibc/aarch64/stdint.h +174 -0
- data/include/libc/glibc/aarch64/sys/epoll.h +88 -0
- data/include/libc/glibc/aarch64/sys/fcntl.h +19 -0
- data/include/libc/glibc/aarch64/sys/select.h +75 -0
- data/include/libc/glibc/aarch64/sys/stat.h +136 -0
- data/include/libc/glibc/aarch64/sys/syscall.h +167 -0
- data/include/libc/glibc/aarch64/sys/time.h +74 -0
- data/include/libc/glibc/aarch64/sys/types.h +127 -0
- data/include/libc/glibc/aarch64/time.h +113 -0
- data/include/libc/glibc/x86_64/ctype.h +108 -0
- data/include/libc/glibc/x86_64/endian.h +54 -0
- data/include/libc/glibc/x86_64/errno.h +154 -0
- data/include/libc/glibc/x86_64/fcntl.h +134 -0
- data/include/libc/glibc/x86_64/inttypes.h +182 -0
- data/include/libc/glibc/x86_64/limits.h +61 -0
- data/include/libc/glibc/x86_64/pthread.h +111 -0
- data/include/libc/glibc/x86_64/setjmp.h +82 -0
- data/include/libc/glibc/x86_64/stdint.h +163 -0
- data/include/libc/glibc/x86_64/sys/epoll.h +91 -0
- data/include/libc/glibc/x86_64/sys/fcntl.h +19 -0
- data/include/libc/glibc/x86_64/sys/select.h +75 -0
- data/include/libc/glibc/x86_64/sys/stat.h +130 -0
- data/include/libc/glibc/x86_64/sys/syscall.h +188 -0
- data/include/libc/glibc/x86_64/sys/time.h +74 -0
- data/include/libc/glibc/x86_64/sys/types.h +123 -0
- data/include/libc/glibc/x86_64/time.h +113 -0
- data/include/libc/grp.h +50 -0
- data/include/libc/langinfo.h +126 -0
- data/include/libc/link.h +18 -0
- data/include/libc/locale.h +83 -0
- data/include/libc/math.h +191 -0
- data/include/libc/netinet/in.h +126 -0
- data/include/libc/netinet/tcp.h +44 -0
- data/include/libc/poll.h +46 -0
- data/include/libc/pwd.h +58 -0
- data/include/libc/regex.h +53 -0
- data/include/libc/sched.h +35 -0
- data/include/libc/signal.h +204 -0
- data/include/libc/stdio.h +157 -0
- data/include/libc/stdlib.h +92 -0
- data/include/libc/string.h +74 -0
- data/include/libc/strings.h +25 -0
- data/include/libc/sys/cdefs.h +126 -0
- data/include/libc/sys/inotify.h +111 -0
- data/include/libc/sys/ioctl.h +36 -0
- data/include/libc/sys/mman.h +65 -0
- data/include/libc/sys/param.h +41 -0
- data/include/libc/sys/resource.h +109 -0
- data/include/libc/sys/socket.h +167 -0
- data/include/libc/sys/statfs.h +82 -0
- data/include/libc/sys/timerfd.h +55 -0
- data/include/libc/sys/uio.h +40 -0
- data/include/libc/sys/un.h +25 -0
- data/include/libc/sys/utsname.h +35 -0
- data/include/libc/sys/wait.h +135 -0
- data/include/libc/termios.h +179 -0
- data/include/libc/unistd.h +194 -0
- data/include/stdalign.h +16 -0
- data/include/stdarg.h +31 -0
- data/include/stdatomic.h +158 -0
- data/include/stdbool.h +15 -0
- data/include/stdckdint.h +28 -0
- data/include/stddef.h +60 -0
- data/include/stdnoreturn.h +18 -0
- data/include/x86intrin.h +16 -0
- data/lib/rubycc/backend/aarch64.rb +1724 -0
- data/lib/rubycc/backend/x86_64.rb +1369 -0
- data/lib/rubycc/compile_error.rb +36 -0
- data/lib/rubycc/compiler.rb +305 -0
- data/lib/rubycc/doctor/builder.rb +151 -0
- data/lib/rubycc/doctor/cli.rb +190 -0
- data/lib/rubycc/doctor/fetcher.rb +84 -0
- data/lib/rubycc/doctor/gemfile.rb +115 -0
- data/lib/rubycc/doctor/verified_gems.rb +114 -0
- data/lib/rubycc/doctor.rb +16 -0
- data/lib/rubycc/driver.rb +463 -0
- data/lib/rubycc/front/ast.rb +528 -0
- data/lib/rubycc/front/constant_evaluator.rb +631 -0
- data/lib/rubycc/front/initializer_resolver.rb +592 -0
- data/lib/rubycc/front/lexeme_reader.rb +460 -0
- data/lib/rubycc/front/lexer.rb +232 -0
- data/lib/rubycc/front/parser.rb +4122 -0
- data/lib/rubycc/front/token.rb +49 -0
- data/lib/rubycc/ir/call_convention.rb +486 -0
- data/lib/rubycc/ir/generator.rb +6036 -0
- data/lib/rubycc/ir/ir.rb +417 -0
- data/lib/rubycc/link/compat_runtime.rb +89 -0
- data/lib/rubycc/link/errors.rb +13 -0
- data/lib/rubycc/link/executable_linker.rb +405 -0
- data/lib/rubycc/link/library_resolver.rb +437 -0
- data/lib/rubycc/link/partial_linker.rb +546 -0
- data/lib/rubycc/link/shared_linker.rb +1732 -0
- data/lib/rubycc/mkmf_shim.rb +83 -0
- data/lib/rubycc/objfile/ar_archive.rb +437 -0
- data/lib/rubycc/objfile/elf_reader.rb +646 -0
- data/lib/rubycc/objfile/elf_writer.rb +891 -0
- data/lib/rubycc/objfile/relocatable_writer.rb +376 -0
- data/lib/rubycc/pkgconf/cli.rb +139 -0
- data/lib/rubycc/pkgconf/errors.rb +39 -0
- data/lib/rubycc/pkgconf/model.rb +28 -0
- data/lib/rubycc/pkgconf/parser.rb +109 -0
- data/lib/rubycc/pkgconf/pkgconf.rb +15 -0
- data/lib/rubycc/pkgconf/resolver.rb +70 -0
- data/lib/rubycc/pkgconf/search_path.rb +38 -0
- data/lib/rubycc/pkgconf/system_path_filter.rb +131 -0
- data/lib/rubycc/preprocess/constant_expression.rb +219 -0
- data/lib/rubycc/preprocess/glibc_version.rb +124 -0
- data/lib/rubycc/preprocess/pp_token.rb +62 -0
- data/lib/rubycc/preprocess/preprocessor.rb +2020 -0
- data/lib/rubycc/preprocess/scanner.rb +290 -0
- data/lib/rubycc/preprocess/token_converter.rb +157 -0
- data/lib/rubycc/rmake/cli.rb +143 -0
- data/lib/rubycc/rmake/errors.rb +71 -0
- data/lib/rubycc/rmake/executor.rb +818 -0
- data/lib/rubycc/rmake/expander.rb +251 -0
- data/lib/rubycc/rmake/makefile.rb +352 -0
- data/lib/rubycc/rmake/model.rb +137 -0
- data/lib/rubycc/rmake/parser.rb +226 -0
- data/lib/rubycc/rmake/rmake.rb +15 -0
- data/lib/rubycc/type.rb +1236 -0
- data/lib/rubycc/version.rb +5 -0
- data/lib/rubycc.rb +36 -0
- data/lib/rubygems_plugin.rb +102 -0
- metadata +219 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubycc
|
|
4
|
+
module Front
|
|
5
|
+
# Abstract syntax tree nodes for the C subset. Each node carries a
|
|
6
|
+
# representative token (`token`) for source-location diagnostics.
|
|
7
|
+
module AST
|
|
8
|
+
# Integer literal. `value` is a Ruby Integer and `type` its Rubycc::Type,
|
|
9
|
+
# fixed by the parser from the constant's base, suffix and magnitude
|
|
10
|
+
# (6.4.4.1) — int/long or their unsigned forms; a character constant is
|
|
11
|
+
# int.
|
|
12
|
+
IntLit = Data.define(:value, :token, :type)
|
|
13
|
+
|
|
14
|
+
# Floating-point literal. `value` is a Ruby Float and `type` its
|
|
15
|
+
# Rubycc::Type — Type::Float for an f/F-suffixed constant, Type::Double
|
|
16
|
+
# otherwise (a plain or l/L-suffixed one) — fixed by the parser from the
|
|
17
|
+
# constant's suffix (6.4.4.2).
|
|
18
|
+
FloatLit = Data.define(:value, :type, :token)
|
|
19
|
+
|
|
20
|
+
# String literal. `value` is the escape-resolved bytes as an ASCII-8BIT
|
|
21
|
+
# String, without the NUL terminator. Its type is char[N+1] (N being the
|
|
22
|
+
# byte count); in every expression context except sizeof it decays to a
|
|
23
|
+
# char * pointing at the string's first byte in .rodata.
|
|
24
|
+
StringLit = Data.define(:value, :token)
|
|
25
|
+
|
|
26
|
+
# Unary operation. `op` is :neg or :not (unary + is folded away by the
|
|
27
|
+
# parser), :addr (the address-of "&") or :deref (the dereference "*").
|
|
28
|
+
Unary = Data.define(:op, :operand, :token)
|
|
29
|
+
|
|
30
|
+
# Binary operation. `op` is one of :add, :sub, :mul, :div, :mod
|
|
31
|
+
# (arithmetic), :and, :or, :xor (bitwise), :shl, :shr (shifts) or
|
|
32
|
+
# :eq, :ne, :lt, :le, :gt, :ge (comparisons yielding int 0/1). The unary
|
|
33
|
+
# bitwise-not "~x" has no op of its own: the parser desugars it to the
|
|
34
|
+
# Binary "x ^ -1", so it reaches the generator as an ordinary :xor.
|
|
35
|
+
Binary = Data.define(:op, :lhs, :rhs, :token)
|
|
36
|
+
|
|
37
|
+
# The comma operator "left, right" (ISO C 6.5.17): `left` is evaluated
|
|
38
|
+
# for its side effects and its value discarded, then `right` is evaluated
|
|
39
|
+
# and its value and type become the whole expression's. Only a context
|
|
40
|
+
# that admits a full expression (a parenthesized expression, a subscript,
|
|
41
|
+
# an expression-statement, the three for-clauses, a control condition, a
|
|
42
|
+
# return, the middle of "?:") ever builds one; a comma that merely
|
|
43
|
+
# separates items (call arguments, declarators) is not this node.
|
|
44
|
+
Comma = Data.define(:left, :right, :token)
|
|
45
|
+
|
|
46
|
+
# Short-circuit "&&" and "||". Kept apart from Binary because both
|
|
47
|
+
# operands must be int-typed and only one side may end up evaluated at
|
|
48
|
+
# run time, unlike every other Binary operator.
|
|
49
|
+
LogicalAnd = Data.define(:lhs, :rhs, :token)
|
|
50
|
+
LogicalOr = Data.define(:lhs, :rhs, :token)
|
|
51
|
+
|
|
52
|
+
# The conditional operator "condition ? then_expr : else_expr".
|
|
53
|
+
# `condition` must be int-typed; `then_expr` and `else_expr` must share
|
|
54
|
+
# the same type (int/int or same-type pointer/pointer).
|
|
55
|
+
Conditional = Data.define(:condition, :then_expr, :else_expr, :token)
|
|
56
|
+
|
|
57
|
+
# Compound assignment "target op= value". `op` is one of :add, :sub,
|
|
58
|
+
# :mul, :div, :mod (the operator without its trailing "="). `target` is
|
|
59
|
+
# a VariableRef, a Subscript or a dereference (Unary with op :deref); the
|
|
60
|
+
# parser rejects any other, non-assignable target.
|
|
61
|
+
CompoundAssignment = Data.define(:op, :target, :value, :token)
|
|
62
|
+
|
|
63
|
+
# Prefix or postfix "++"/"--". `op` is :add for "++" and :sub for "--".
|
|
64
|
+
# `prefix` is true for "++x"/"--x" and false for "x++"/"x--". `target`
|
|
65
|
+
# is a VariableRef, a Subscript or a dereference, exactly like
|
|
66
|
+
# CompoundAssignment.
|
|
67
|
+
IncDec = Data.define(:op, :target, :prefix, :token)
|
|
68
|
+
|
|
69
|
+
# `return <expr>;`, or `return;` with `expr` nil (a void return).
|
|
70
|
+
Return = Data.define(:expr, :token)
|
|
71
|
+
|
|
72
|
+
# A local variable declaration. `type` is the declared Rubycc::Type
|
|
73
|
+
# (int or a pointer). `initializer` is an expression node or nil when the
|
|
74
|
+
# declarator has no "= <expr>" part. `const` is true when the object is
|
|
75
|
+
# top-level const-qualified (the only qualification M1 tracks), which the
|
|
76
|
+
# generator rejects writes against. `storage` records the storage-class
|
|
77
|
+
# specifier (nil, :static or :extern) for Phase B; the generator does not
|
|
78
|
+
# consume it yet. `alignas` is the boundary an _Alignas specifier asked for
|
|
79
|
+
# (6.7.5), or nil when the object takes its type's own alignment.
|
|
80
|
+
VariableDecl = Data.define(:name, :type, :initializer, :token, :const, :storage, :alignas)
|
|
81
|
+
|
|
82
|
+
# A file-scope (global) variable declaration. `type` is the declared
|
|
83
|
+
# Rubycc::Type (int, char, a pointer, a one-dimensional array or a
|
|
84
|
+
# struct/union). At most one of two initializer fields is set:
|
|
85
|
+
# `initializer_value` is a plain scalar-integer initializer folded to a
|
|
86
|
+
# Ruby Integer on the spot (the common "int g = 1 + 2;" case, kept folded
|
|
87
|
+
# so nothing downstream re-evaluates it), while `initializer_node` carries
|
|
88
|
+
# a still-unfolded initializer the generator lowers itself — a brace
|
|
89
|
+
# initializer-list (AST::InitializerList) for an aggregate or scalar, a
|
|
90
|
+
# string literal for a char array, or an address constant for a pointer
|
|
91
|
+
# (a null pointer, "&other_global", a decayed global array name or a
|
|
92
|
+
# string literal). Both are nil for an uninitialized global, which lands
|
|
93
|
+
# in .bss. `const` and `storage` carry the same top-level const flag and
|
|
94
|
+
# storage-class specifier (nil/:static/:extern) as VariableDecl; the
|
|
95
|
+
# generator diagnoses writes against a const global but does not yet act
|
|
96
|
+
# on the storage class (Phase B). `alignas` carries the same _Alignas
|
|
97
|
+
# boundary VariableDecl does.
|
|
98
|
+
GlobalDecl = Data.define(:name, :type, :initializer_value, :initializer_node, :token, :const, :storage,
|
|
99
|
+
:alignas)
|
|
100
|
+
|
|
101
|
+
# A brace-enclosed initializer "{ ... }" (ISO C 6.7.9). `items` is the
|
|
102
|
+
# ordered list of InitItem, one per comma-separated element (a trailing
|
|
103
|
+
# comma adds none). Appears as a VariableDecl/GlobalDecl initializer, or
|
|
104
|
+
# nested as an InitItem's value; the resolver walks it against the
|
|
105
|
+
# object's type to place each scalar. `token` is the opening brace.
|
|
106
|
+
InitializerList = Data.define(:items, :token)
|
|
107
|
+
|
|
108
|
+
# One element of an initializer-list. `designators` is the (possibly
|
|
109
|
+
# empty) leading designator chain — ArrayDesignator / MemberDesignator
|
|
110
|
+
# nodes, so ".a.b = x" and "[1].m = y" carry two — that redirects the
|
|
111
|
+
# current object before `value` initializes it; `value` is either an
|
|
112
|
+
# expression node or a nested InitializerList.
|
|
113
|
+
InitItem = Data.define(:designators, :value)
|
|
114
|
+
|
|
115
|
+
# An array designator "[constant-expression]" (6.7.9). `index` is the
|
|
116
|
+
# bracketed constant folded to a Ruby Integer at parse time; `token` is
|
|
117
|
+
# the "[" for diagnostics (an out-of-range index).
|
|
118
|
+
ArrayDesignator = Data.define(:index, :token)
|
|
119
|
+
|
|
120
|
+
# A member designator ".identifier" (6.7.9). `name` is the member's name;
|
|
121
|
+
# `token` is the "." for diagnostics (an unknown member).
|
|
122
|
+
MemberDesignator = Data.define(:name, :token)
|
|
123
|
+
|
|
124
|
+
# A reference to a local variable by name.
|
|
125
|
+
VariableRef = Data.define(:name, :token)
|
|
126
|
+
|
|
127
|
+
# A subscript `target[index]` (postfix "[" expression "]"). Both `target`
|
|
128
|
+
# and `index` are expression nodes; the generator lowers it as
|
|
129
|
+
# "*(target + index)" and type-checks that `target` is an array/pointer.
|
|
130
|
+
Subscript = Data.define(:target, :index, :token)
|
|
131
|
+
|
|
132
|
+
# A struct member access. `base` is the postfix-expression on the left,
|
|
133
|
+
# `member` the identifier String on the right and `arrow` false for the
|
|
134
|
+
# "." form (base is the struct itself) or true for the "->" form (base is
|
|
135
|
+
# a pointer to the struct, so "p->m" means "(*p).m"). Both forms are
|
|
136
|
+
# lvalues: the generator lowers either to the base struct's address plus
|
|
137
|
+
# the member's constant offset, then reads or writes through it.
|
|
138
|
+
MemberAccess = Data.define(:base, :member, :arrow, :token)
|
|
139
|
+
|
|
140
|
+
# `sizeof operand`: the byte size of a unary-expression's result type.
|
|
141
|
+
# `operand` is an expression node; the array-to-pointer decay does not
|
|
142
|
+
# apply to it, so `sizeof a` measures the whole array.
|
|
143
|
+
SizeofExpr = Data.define(:operand, :token)
|
|
144
|
+
|
|
145
|
+
# `sizeof ( type-name )`: the byte size of a written type ("int",
|
|
146
|
+
# "int *", ...). `type` is the resolved Rubycc::Type.
|
|
147
|
+
SizeofType = Data.define(:type, :token)
|
|
148
|
+
|
|
149
|
+
# `_Alignof ( type-name )` (ISO C 6.5.3.4): the alignment requirement, in
|
|
150
|
+
# bytes, of a written type. `type` is the resolved Rubycc::Type. Only the
|
|
151
|
+
# parenthesized type-name form exists; unlike sizeof there is no operand
|
|
152
|
+
# form. The result folds to a size_t (unsigned long) constant, exactly
|
|
153
|
+
# like SizeofType, and is rejected for a void, function or incomplete type.
|
|
154
|
+
AlignofType = Data.define(:type, :token)
|
|
155
|
+
|
|
156
|
+
# A cast "( type-name ) operand" (ISO C 6.5.4). `type` is the resolved
|
|
157
|
+
# Rubycc::Type the operand is converted to and `operand` is the
|
|
158
|
+
# cast-expression to its right. The generator settles which conversions
|
|
159
|
+
# are legal (arithmetic reinterpretation, a pointer retag, discarding a
|
|
160
|
+
# value with "(void)", a null-pointer-constant cast) and emits any code a
|
|
161
|
+
# narrowing needs; the parser only builds the node.
|
|
162
|
+
Cast = Data.define(:type, :operand, :token)
|
|
163
|
+
|
|
164
|
+
# A compound literal "( type-name ) { initializer-list }" (ISO C 6.5.2.5).
|
|
165
|
+
# `type` is the resolved Rubycc::Type of the unnamed object the literal
|
|
166
|
+
# denotes, with any inferred "[]" array bound already completed by the
|
|
167
|
+
# parser (so "(int[]){1,2,3}" carries int[3]); `initializer` is the
|
|
168
|
+
# AST::InitializerList that fills it. In block scope the object has
|
|
169
|
+
# automatic storage lasting the enclosing block, so the literal is an
|
|
170
|
+
# lvalue: the generator lays it out on a stack object, initializes it, and
|
|
171
|
+
# yields the object's value the same way a variable of `type` would (a
|
|
172
|
+
# struct/union as its base address, an array decayed to an element pointer,
|
|
173
|
+
# a scalar as a load) — which is what lets "&(T){...}", "(T){...}.member",
|
|
174
|
+
# a by-value argument and an assignment right-hand side all work. A
|
|
175
|
+
# compound literal in a static-storage-duration (file-scope) initializer is
|
|
176
|
+
# diagnosed as unsupported by the generator. `token` is the opening "(".
|
|
177
|
+
CompoundLiteral = Data.define(:type, :initializer, :token)
|
|
178
|
+
|
|
179
|
+
# A null pointer constant (ISO C11 6.3.2.3p3): "An integer constant
|
|
180
|
+
# expression with the value 0, or such an expression cast to type
|
|
181
|
+
# void *, is called a null pointer constant." #integer_constant_zero?
|
|
182
|
+
# below recognizes the first alternative on its own — that is exactly
|
|
183
|
+
# ConstantEvaluator's notion of an expression that folds to the integer
|
|
184
|
+
# 0, which already covers a plain integer literal 0, a character
|
|
185
|
+
# constant like '\0' (the lexer lowers it to an IntLit 0), an
|
|
186
|
+
# enumeration constant of value 0 (the parser folds a reference to one
|
|
187
|
+
# into an IntLit 0 on the spot, see #parse_primary_expression), a purely
|
|
188
|
+
# arithmetic fold such as "1 - 1", and — because 6.6p6 allows a cast to
|
|
189
|
+
# an integer type inside an integer constant expression — any of those
|
|
190
|
+
# cast to another integer type, such as "(unsigned long)0" or
|
|
191
|
+
# "(char)0". A cast to a *floating* type is never allowed in an integer
|
|
192
|
+
# constant expression (6.6p6 restricts a constant-expression cast to
|
|
193
|
+
# converting to an integer type, outside sizeof/alignof/_Alignof), so
|
|
194
|
+
# "(double)0" is correctly excluded here — matching the "invalid
|
|
195
|
+
# operands" gcc gives comparing it against a pointer, rather than the
|
|
196
|
+
# silent accept a floating cast would otherwise get. The second
|
|
197
|
+
# alternative — that whole expression additionally cast to "void *" — is
|
|
198
|
+
# the explicit Cast case below, since ConstantEvaluator itself only
|
|
199
|
+
# folds a cast to an *integer* destination.
|
|
200
|
+
#
|
|
201
|
+
# A null pointer constant converts implicitly to any pointer type in an
|
|
202
|
+
# assignment, an initializer, an argument, a return, an "=="/"!="
|
|
203
|
+
# comparison and the arms of "?:".
|
|
204
|
+
def self.null_pointer_constant?(node)
|
|
205
|
+
return true if integer_constant_zero?(node)
|
|
206
|
+
|
|
207
|
+
node.is_a?(Cast) && node.type.pointer? && node.type.target.void? &&
|
|
208
|
+
integer_constant_zero?(node.operand)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Whether `node` is an integer constant expression (6.6) whose value is
|
|
212
|
+
# 0, via the shared ConstantEvaluator — not just a syntactic IntLit, so
|
|
213
|
+
# a foldable expression (an enum constant, "1 - 1", a cast to another
|
|
214
|
+
# integer type) is recognized too. Not a constant expression at all (a
|
|
215
|
+
# variable, a call, ...) or a division by a folded-zero divisor rescues
|
|
216
|
+
# to false rather than propagating, since this is a yes/no predicate,
|
|
217
|
+
# not a context that should surface either as a diagnostic.
|
|
218
|
+
def self.integer_constant_zero?(node)
|
|
219
|
+
ConstantEvaluator.evaluate(node).zero?
|
|
220
|
+
rescue ConstantEvaluator::NotConstant, ConstantEvaluator::DivisionByZero
|
|
221
|
+
false
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Simple assignment `target = value`. `target` is a VariableRef or a
|
|
225
|
+
# dereference (Unary with op :deref); the parser rejects any other,
|
|
226
|
+
# non-assignable target.
|
|
227
|
+
Assignment = Data.define(:target, :value, :token)
|
|
228
|
+
|
|
229
|
+
# An expression evaluated for its side effects; the value is discarded.
|
|
230
|
+
ExpressionStmt = Data.define(:expr, :token)
|
|
231
|
+
|
|
232
|
+
# The empty statement ";".
|
|
233
|
+
EmptyStmt = Data.define(:token)
|
|
234
|
+
|
|
235
|
+
# A GNU inline-assembly statement in its only accepted (degenerate) form
|
|
236
|
+
# (DESIGN R7): `__asm__` with optional volatile qualifiers and an empty
|
|
237
|
+
# template, e.g. `__asm__ volatile("" ::: "memory");`. The parser has
|
|
238
|
+
# already verified the template is empty and no real operand appears, so
|
|
239
|
+
# nothing is carried but the keyword token; it lowers to no code at all —
|
|
240
|
+
# rubycc never reorders, so a memory-clobber barrier is a natural no-op.
|
|
241
|
+
InlineAsm = Data.define(:token)
|
|
242
|
+
|
|
243
|
+
# `if (condition) then_stmt` with an optional `else else_stmt`.
|
|
244
|
+
# `else_stmt` is nil when there is no else clause.
|
|
245
|
+
If = Data.define(:condition, :then_stmt, :else_stmt, :token)
|
|
246
|
+
|
|
247
|
+
# A compound-statement "{ block-item* }" introducing a new scope.
|
|
248
|
+
# `items` is a flat array of declaration/statement nodes.
|
|
249
|
+
Block = Data.define(:items, :token)
|
|
250
|
+
|
|
251
|
+
# A GNU statement expression "( { block-item* } )" (a GCC extension, not
|
|
252
|
+
# ISO C). `body` is the AST::Block the parentheses wrap around a compound
|
|
253
|
+
# statement; the braces introduce the block's own scope, just like any
|
|
254
|
+
# compound-statement. When the block's last block-item is an
|
|
255
|
+
# expression-statement, that expression's value and type are the whole
|
|
256
|
+
# construct's; otherwise (an empty block, or a last item that is a
|
|
257
|
+
# declaration, a loop, a bare ";" and so on) the construct is void.
|
|
258
|
+
StatementExpr = Data.define(:body, :token)
|
|
259
|
+
|
|
260
|
+
# `while (condition) body`
|
|
261
|
+
While = Data.define(:condition, :body, :token)
|
|
262
|
+
|
|
263
|
+
# `do body while (condition);`
|
|
264
|
+
DoWhile = Data.define(:body, :condition, :token)
|
|
265
|
+
|
|
266
|
+
# `for (init; condition; step) body`. `init` is an array of
|
|
267
|
+
# VariableDecl (from a declaration clause), an expression node (from an
|
|
268
|
+
# expression clause) or nil (clause omitted). `condition` and `step`
|
|
269
|
+
# are expression nodes or nil when omitted.
|
|
270
|
+
For = Data.define(:init, :condition, :step, :body, :token)
|
|
271
|
+
|
|
272
|
+
# `break;` — targets the innermost enclosing iteration-statement or,
|
|
273
|
+
# once switch exists, the innermost enclosing switch.
|
|
274
|
+
Break = Data.define(:token)
|
|
275
|
+
|
|
276
|
+
# `continue;` — targets the innermost enclosing iteration-statement,
|
|
277
|
+
# passing straight through any switch that sits between it and the loop.
|
|
278
|
+
Continue = Data.define(:token)
|
|
279
|
+
|
|
280
|
+
# `switch (control) body` (ISO C 6.8.4.2). `control` is the controlling
|
|
281
|
+
# expression (an integer type). `body` is a single statement — usually a
|
|
282
|
+
# compound-statement — inside which `case`/`default` labels mark the entry
|
|
283
|
+
# points the generator lowers to a comparison chain. A case label may sit
|
|
284
|
+
# at any depth of a nested statement in `body`; the ones that belong to
|
|
285
|
+
# this switch are exactly those not enclosed by a nested switch.
|
|
286
|
+
Switch = Data.define(:control, :body, :token)
|
|
287
|
+
|
|
288
|
+
# A `case <constant>: statement` label (6.8.1). `value` is the case
|
|
289
|
+
# constant folded to a Ruby Integer at parse time (an integer or character
|
|
290
|
+
# constant, optionally signed — general constant-expression evaluation
|
|
291
|
+
# arrives in a later step). `body` is the labeled statement. A Case only
|
|
292
|
+
# has meaning inside a switch; the generator diagnoses one that is not.
|
|
293
|
+
Case = Data.define(:value, :body, :token)
|
|
294
|
+
|
|
295
|
+
# A `default: statement` label (6.8.1). `body` is the labeled statement.
|
|
296
|
+
# Like Case it is only meaningful inside a switch, and at most one may
|
|
297
|
+
# appear per switch.
|
|
298
|
+
Default = Data.define(:body, :token)
|
|
299
|
+
|
|
300
|
+
# A `goto identifier;` jump (6.8.6.1). `label` is the target label's name.
|
|
301
|
+
# The target may be defined anywhere in the same function, before or after
|
|
302
|
+
# the goto; the generator resolves the name against a function-scoped label
|
|
303
|
+
# table and diagnoses a jump to an undefined label at the function's end.
|
|
304
|
+
Goto = Data.define(:label, :token)
|
|
305
|
+
|
|
306
|
+
# A labeled statement `identifier: statement` (6.8.1), the target of a
|
|
307
|
+
# goto. `name` is the label's name and `body` the statement it prefixes
|
|
308
|
+
# (often the empty statement, as in "end: ;"). Distinguished from an
|
|
309
|
+
# ordinary expression-statement by two tokens of lookahead — an identifier
|
|
310
|
+
# immediately followed by ":".
|
|
311
|
+
Label = Data.define(:name, :body, :token)
|
|
312
|
+
|
|
313
|
+
# A function call "callee ( args )" (ISO C 6.5.2.2). `callee` is the
|
|
314
|
+
# postfix-expression being called — an ordinary identifier for a direct
|
|
315
|
+
# call ("f(x)"), but equally a dereference ("(*fp)(x)"), a subscript
|
|
316
|
+
# ("table[i](x)"), a member access ("s.fp(x)") or any expression that
|
|
317
|
+
# yields a function or a function pointer. `args` is an array of
|
|
318
|
+
# expression nodes (the argument-expression-list), evaluated left to
|
|
319
|
+
# right. The generator settles whether the callee is a direct function
|
|
320
|
+
# designator (a plain :call) or a pointer value (an indirect call).
|
|
321
|
+
Call = Data.define(:callee, :args, :token)
|
|
322
|
+
|
|
323
|
+
# "__builtin_va_start ( ap , last )" (the initializer of a variable
|
|
324
|
+
# argument list). `ap` is the va_list expression to initialize; `last_name`
|
|
325
|
+
# is the identifier String naming the function's last fixed parameter,
|
|
326
|
+
# which ISO C requires so the variable part can be located just past it.
|
|
327
|
+
# `token` is the builtin keyword, locating every diagnostic (a fixed-arity
|
|
328
|
+
# enclosing function, a wrong last-parameter name, a bad `ap` type). The
|
|
329
|
+
# expression's value is void — it is only ever an expression-statement.
|
|
330
|
+
VaStart = Data.define(:ap, :last_name, :token)
|
|
331
|
+
|
|
332
|
+
# "__builtin_va_arg ( ap , type-name )": fetches the next variable argument
|
|
333
|
+
# and advances `ap`. `ap` is the va_list expression and `type` is the
|
|
334
|
+
# resolved Rubycc::Type the argument is read as (an int/long/unsigned/
|
|
335
|
+
# pointer object type; a promotable or aggregate type is diagnosed). The
|
|
336
|
+
# whole expression's value and type are that argument.
|
|
337
|
+
VaArg = Data.define(:ap, :type, :token)
|
|
338
|
+
|
|
339
|
+
# "__builtin_va_end ( ap )": ends traversal of `ap`. On System V this is a
|
|
340
|
+
# no-op beyond type-checking the operand, so the node carries only the
|
|
341
|
+
# va_list expression and the keyword token; its value is void.
|
|
342
|
+
VaEnd = Data.define(:ap, :token)
|
|
343
|
+
|
|
344
|
+
# "__builtin_va_copy ( dest , src )": copies the traversal state of `src`
|
|
345
|
+
# into `dest` so the two can be walked independently (7.16.1.2). Both are
|
|
346
|
+
# va_list expressions; the generator lowers the copy to a whole-tag move
|
|
347
|
+
# between their (decayed) addresses. The value is void, like va_end.
|
|
348
|
+
VaCopy = Data.define(:dest, :src, :token)
|
|
349
|
+
|
|
350
|
+
# "__builtin_expect ( exp , c )": gcc's branch-prediction hint, typed
|
|
351
|
+
# `long(long, long)`. `exp` is the tested expression and `c` the value it
|
|
352
|
+
# is expected to take. rubycc has no optimizer, so the hint carries no
|
|
353
|
+
# weight: both operands are evaluated (left to right, `c` for its side
|
|
354
|
+
# effects) and the whole expression is `exp` converted to `long`. `token`
|
|
355
|
+
# is the builtin keyword.
|
|
356
|
+
BuiltinExpect = Data.define(:exp, :c, :token)
|
|
357
|
+
|
|
358
|
+
# "__builtin_alloca ( n )": reserves `n` bytes of automatic storage on the
|
|
359
|
+
# stack, released when the enclosing *function* returns. `size` is the
|
|
360
|
+
# byte-count expression (converted to size_t) and `token` the keyword; the
|
|
361
|
+
# whole expression's value is the block's base address, a `void *` the ABI
|
|
362
|
+
# keeps 16-byte aligned.
|
|
363
|
+
BuiltinAlloca = Data.define(:size, :token)
|
|
364
|
+
|
|
365
|
+
# "__builtin_offsetof ( type-name , member-designator )": the byte offset of
|
|
366
|
+
# a member within a struct/union, folded to a size_t constant. Unlike the
|
|
367
|
+
# traditional "((size_t)&(((t*)0)->m))" macro, it is a genuine
|
|
368
|
+
# constant-expression, so it holds in a static initializer, an array bound
|
|
369
|
+
# or a case label — the reason gcc/clang provide it. `type` is the resolved
|
|
370
|
+
# aggregate type; `designator` is the parsed member-designator, a non-empty
|
|
371
|
+
# array whose first element is always an OffsetofMember (the leading member
|
|
372
|
+
# name carries no "."). Each element is either an OffsetofMember (".name",
|
|
373
|
+
# or the leading bare name) or an OffsetofIndex ("[ constant-expression ]"),
|
|
374
|
+
# so a nested field ("a.b[2].c") reaches its target one element at a time.
|
|
375
|
+
# `token` is the builtin keyword, locating every diagnostic.
|
|
376
|
+
BuiltinOffsetof = Data.define(:type, :designator, :token)
|
|
377
|
+
|
|
378
|
+
# One member step of a __builtin_offsetof designator: the member named
|
|
379
|
+
# `name` of the aggregate reached so far. `token` is the member identifier,
|
|
380
|
+
# for a "no member named ..." diagnostic.
|
|
381
|
+
OffsetofMember = Data.define(:name, :token)
|
|
382
|
+
|
|
383
|
+
# One subscript step of a __builtin_offsetof designator: element `index`
|
|
384
|
+
# (a constant-expression node) of the array reached so far. `token` is the
|
|
385
|
+
# "[" token, for a "subscript of non-array" diagnostic.
|
|
386
|
+
OffsetofIndex = Data.define(:index, :token)
|
|
387
|
+
|
|
388
|
+
# "__builtin_constant_p ( expr )": gcc's compile-time-constant test, typed
|
|
389
|
+
# int. `expr` is the (unevaluated) operand and `token` the builtin keyword.
|
|
390
|
+
# The whole expression folds to 1 when `expr` reduces to a compile-time
|
|
391
|
+
# constant and 0 otherwise — including when it references a variable or a
|
|
392
|
+
# function call, which is not an error here (unlike an ordinary constant
|
|
393
|
+
# expression) but simply yields 0. `expr` is never evaluated, so it
|
|
394
|
+
# produces no code and no side effects.
|
|
395
|
+
BuiltinConstantP = Data.define(:expr, :token)
|
|
396
|
+
|
|
397
|
+
# "__builtin_ctz/ctzll/clz/clzll ( x )": counts the trailing (ctz) or
|
|
398
|
+
# leading (clz) zero bits of an integer, typed int. `operand` is the value
|
|
399
|
+
# scanned, `direction` is :forward for ctz or :reverse for clz, and `width`
|
|
400
|
+
# is the operand's byte width (4 for the plain form, 8 for the "ll" form).
|
|
401
|
+
# `x == 0` is undefined behavior (gcc), so no zero handling is implied.
|
|
402
|
+
# `token` is the builtin keyword.
|
|
403
|
+
BuiltinBitScan = Data.define(:operand, :direction, :width, :token)
|
|
404
|
+
|
|
405
|
+
# "__builtin_add/sub/mul_overflow ( a , b , res )": computes "a op b" with
|
|
406
|
+
# infinite precision, stores the result converted to the type `res` points
|
|
407
|
+
# at (wrapping or truncating like any integer conversion), and yields int 1
|
|
408
|
+
# when that conversion lost the value and 0 when it did not. `op` is :add,
|
|
409
|
+
# :sub or :mul and `args` holds the three argument expressions as written.
|
|
410
|
+
# The parser checks only the argument *count*; the operand types (both
|
|
411
|
+
# integers, `res` a pointer to an integer) need resolved types and are the
|
|
412
|
+
# generator's to diagnose. `token` is the builtin keyword.
|
|
413
|
+
#
|
|
414
|
+
# The two operands keep their own types — no usual arithmetic conversion
|
|
415
|
+
# runs between them, which is the whole point of the builtin: "int -1" plus
|
|
416
|
+
# "unsigned 1" is 0 in infinite precision, not UINT_MAX.
|
|
417
|
+
BuiltinOverflow = Data.define(:op, :args, :token)
|
|
418
|
+
|
|
419
|
+
# One of gcc's __atomic_* builtins. `kind` names the operation — :load,
|
|
420
|
+
# :store, :exchange, :compare_exchange, :fetch_add, :fetch_sub,
|
|
421
|
+
# :add_fetch, :sub_fetch, :or_fetch or :fence — and `args`
|
|
422
|
+
# holds the argument expressions exactly as written, including the trailing
|
|
423
|
+
# memory-order argument(s) and __atomic_compare_exchange_n's `weak` flag.
|
|
424
|
+
# The parser checks only the argument *count* here; the operand types (and
|
|
425
|
+
# the 4-or-8-byte width restriction) are the generator's to diagnose, since
|
|
426
|
+
# they need the resolved types. `token` is the builtin keyword.
|
|
427
|
+
#
|
|
428
|
+
# The memory-order arguments are carried through but never inspected: every
|
|
429
|
+
# operation is lowered at the strongest order (see
|
|
430
|
+
# IR::Generator#gen_builtin_atomic), which is why they need no constant
|
|
431
|
+
# folding here.
|
|
432
|
+
BuiltinAtomic = Data.define(:kind, :args, :token)
|
|
433
|
+
|
|
434
|
+
# One of gcc's legacy __sync_* builtins. `kind` names the operation —
|
|
435
|
+
# :fetch_add, :fetch_sub, :add_fetch, :sub_fetch, :or_fetch, :exchange,
|
|
436
|
+
# :release, :fence, :bool_compare_and_swap or :val_compare_and_swap — and
|
|
437
|
+
# `args` holds the argument expressions exactly as written. As with
|
|
438
|
+
# BuiltinAtomic the parser checks only the argument *count*; the operand
|
|
439
|
+
# types and the width restriction need resolved types and belong to the
|
|
440
|
+
# generator. `token` is the builtin keyword.
|
|
441
|
+
#
|
|
442
|
+
# This is a node of its own rather than a flag on BuiltinAtomic because the
|
|
443
|
+
# two families' argument *layouts* differ: the __sync_* forms are full
|
|
444
|
+
# barriers by definition and so carry no memory-order argument at all,
|
|
445
|
+
# while the compare-and-swap pair takes its expected value directly instead
|
|
446
|
+
# of through a pointer. Sharing a node would invite lowering code written
|
|
447
|
+
# for one layout to read the other's arguments by index.
|
|
448
|
+
BuiltinSync = Data.define(:kind, :args, :token)
|
|
449
|
+
|
|
450
|
+
# "__builtin_unreachable ()": marks a point control never reaches, typed
|
|
451
|
+
# void. rubycc performs no optimization, so it lowers to no code at all —
|
|
452
|
+
# its only role is to let constructs like CRuby's UNREACHABLE_RETURN
|
|
453
|
+
# ("(__builtin_unreachable(), value)") compile. `token` is the keyword.
|
|
454
|
+
BuiltinUnreachable = Data.define(:token)
|
|
455
|
+
|
|
456
|
+
# A single function parameter. `name` is the identifier String, or nil
|
|
457
|
+
# for an unnamed parameter in a prototype (e.g. "int f(int, int);").
|
|
458
|
+
# `type` is the parameter's Rubycc::Type (int, char or a pointer; never
|
|
459
|
+
# void, except as a pointer's target). `const` is true when the parameter
|
|
460
|
+
# object is top-level const-qualified ("int f(const int x)"), computed
|
|
461
|
+
# after the array/function-to-pointer adjustment, so the generator can
|
|
462
|
+
# reject writes to it.
|
|
463
|
+
#
|
|
464
|
+
# `incoming_type` is the type the *caller* hands over when that differs
|
|
465
|
+
# from the declared one, and nil (the common case) when the two coincide.
|
|
466
|
+
# Only an old-style (identifier-list) definition can separate them: such a
|
|
467
|
+
# function has no prototype, so its arguments arrive default-argument
|
|
468
|
+
# promoted (6.9.1p10 with 6.5.2.2p6) — a `float` parameter is passed a
|
|
469
|
+
# `double`, a `char`/`short`/`_Bool` parameter an `int` — and the body
|
|
470
|
+
# still sees the narrow object it declared. #abi_type is what the calling
|
|
471
|
+
# convention and the function's own type are built from; `type` is what
|
|
472
|
+
# the body reads and writes.
|
|
473
|
+
Parameter = Data.define(:name, :type, :token, :const, :incoming_type) do
|
|
474
|
+
def initialize(name:, type:, token:, const:, incoming_type: nil)
|
|
475
|
+
super
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
# The type this parameter occupies an ABI slot as.
|
|
479
|
+
def abi_type
|
|
480
|
+
incoming_type || type
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
# A function prototype (a bare declaration with no body), e.g.
|
|
485
|
+
# "int f(int a, int b);". `return_type` is the declared Rubycc::Type
|
|
486
|
+
# (int, char, void or a pointer). `params` is an array of Parameter.
|
|
487
|
+
# `storage` records the storage-class specifier (nil/:static/:extern) for
|
|
488
|
+
# Phase B; `inline` is accepted and folded away by the parser and left off
|
|
489
|
+
# here since the generator has no use for it yet. `variadic` is true when
|
|
490
|
+
# the prototype ends in "..." ("int printf(const char *, ...);"), in which
|
|
491
|
+
# case `params` holds only the fixed, named parameters.
|
|
492
|
+
FunctionDecl = Data.define(:name, :return_type, :params, :token, :storage, :variadic)
|
|
493
|
+
|
|
494
|
+
# A function definition. `return_type` is the declared Rubycc::Type
|
|
495
|
+
# (int, char, void or a pointer). `params` is an array of Parameter
|
|
496
|
+
# (each with a non-nil name) and `body` is an array of statement nodes.
|
|
497
|
+
# `storage` records the storage-class specifier (nil/:static/:extern) for
|
|
498
|
+
# Phase B, the generator not yet acting on it. `variadic` is true when the
|
|
499
|
+
# parameter list ends in "..." (the variable part is reachable only through
|
|
500
|
+
# va_* in Phase B; a body that ignores it compiles and runs already).
|
|
501
|
+
FunctionDef = Data.define(:name, :return_type, :params, :body, :token, :storage, :variadic)
|
|
502
|
+
|
|
503
|
+
# What a function's `__attribute__((constructor))` /
|
|
504
|
+
# `__attribute__((destructor))` asked for. Each field is nil when that
|
|
505
|
+
# attribute never appeared, or the priority it carried — the run-order
|
|
506
|
+
# number, with the parser's DEFAULT_INIT_PRIORITY standing for the
|
|
507
|
+
# unnumbered spelling. A function may be both, as
|
|
508
|
+
# "__attribute__((constructor,destructor))" is.
|
|
509
|
+
InitAttributes = Data.define(:constructor, :destructor)
|
|
510
|
+
|
|
511
|
+
# Whole translation unit. `functions` is an array of FunctionDef,
|
|
512
|
+
# FunctionDecl (prototype) and GlobalDecl (file-scope variable) nodes in
|
|
513
|
+
# source order. `init_attributes` maps a function name to the
|
|
514
|
+
# InitAttributes its declarations carried; it is a property of the unit
|
|
515
|
+
# rather than of any one node because the attribute may be written on a
|
|
516
|
+
# prototype that precedes or follows the definition (see
|
|
517
|
+
# Parser#register_init_attributes). Names never defined here appear in it
|
|
518
|
+
# too and are dropped by the generator. `visibility_attributes` maps an
|
|
519
|
+
# identifier to an explicit GNU ELF visibility; it is kept at program
|
|
520
|
+
# scope because an exported declaration often precedes its definition.
|
|
521
|
+
Program = Data.define(:functions, :init_attributes, :visibility_attributes) do
|
|
522
|
+
def initialize(functions:, init_attributes: {}, visibility_attributes: {})
|
|
523
|
+
super
|
|
524
|
+
end
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
end
|
|
528
|
+
end
|