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,631 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "ast"
|
|
4
|
+
|
|
5
|
+
module Rubycc
|
|
6
|
+
module Front
|
|
7
|
+
# Evaluates a constant-expression (ISO C 6.6, "conditional-expression" with
|
|
8
|
+
# no assignment, increment/decrement, function call or comma reachable at
|
|
9
|
+
# run time) to a Ruby Integer. Given an expression AST node it walks the
|
|
10
|
+
# node straight away, with no dependency on the Parser or on any symbol
|
|
11
|
+
# table: every enum constant and typedef name the parser might have folded
|
|
12
|
+
# into the tree is already gone by the time this sees it (an enum
|
|
13
|
+
# reference is an ordinary IntLit, see AST::Program), so evaluating the
|
|
14
|
+
# AST alone is enough. A case label, an enumerator, a global initializer
|
|
15
|
+
# and an array bound all reduce their expression through this one
|
|
16
|
+
# evaluator; the preprocessor's "#if" (a later step) is meant to reuse it
|
|
17
|
+
# unchanged, which is why it never touches source positions beyond the
|
|
18
|
+
# token each node already carries.
|
|
19
|
+
#
|
|
20
|
+
# M1 keeps the arithmetic itself simple: every intermediate result is an
|
|
21
|
+
# unbounded Ruby Integer, and a declared type's width/signedness is only
|
|
22
|
+
# brought to bear at a Cast (see #evaluate_cast) — ordinary arithmetic
|
|
23
|
+
# never wraps the way a real `unsigned int` would overflow. No construct
|
|
24
|
+
# this subset admits in a constant expression actually depends on that
|
|
25
|
+
# wraparound yet, so the gap is left open rather than modelled.
|
|
26
|
+
class ConstantEvaluator
|
|
27
|
+
# Raised when some reachable part of the expression is not a
|
|
28
|
+
# constant-expression at all — a variable reference, a function call, an
|
|
29
|
+
# assignment, an increment/decrement, `sizeof` of an expression, or a
|
|
30
|
+
# comma. `token` is the offending node's token, for the caller to build a
|
|
31
|
+
# diagnostic naming its own context ("case label ...", "array size ...").
|
|
32
|
+
class NotConstant < StandardError
|
|
33
|
+
attr_reader :token
|
|
34
|
+
|
|
35
|
+
def initialize(token)
|
|
36
|
+
@token = token
|
|
37
|
+
super("expression is not a constant expression")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Raised when a division or remainder operator that *is* reached at
|
|
42
|
+
# evaluation time (one on the taken side of "&&"/"||"/"?:" — the other
|
|
43
|
+
# side is never evaluated, so a zero divisor there raises nothing) has a
|
|
44
|
+
# zero right operand. `token` is the operator's token.
|
|
45
|
+
class DivisionByZero < StandardError
|
|
46
|
+
attr_reader :token
|
|
47
|
+
|
|
48
|
+
def initialize(token)
|
|
49
|
+
@token = token
|
|
50
|
+
super("division by zero in constant expression")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Raised when a __builtin_offsetof designator cannot name a byte offset:
|
|
55
|
+
# its type is not a struct/union or is incomplete, a step names no such
|
|
56
|
+
# member, a subscript step applies to a non-array member, or the target is
|
|
57
|
+
# a bit-field (which has no addressable byte offset). It is a NotConstant
|
|
58
|
+
# so every context that already reports a non-constant expression catches
|
|
59
|
+
# it too; `detail` carries the specific wording for a caller (the
|
|
60
|
+
# generator) that can surface it, over NotConstant's generic message.
|
|
61
|
+
class OffsetofError < NotConstant
|
|
62
|
+
attr_reader :detail
|
|
63
|
+
|
|
64
|
+
def initialize(token, detail)
|
|
65
|
+
@detail = detail
|
|
66
|
+
super(token)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
BINARY_OPERATIONS = {
|
|
71
|
+
add: ->(a, b) { a + b },
|
|
72
|
+
sub: ->(a, b) { a - b },
|
|
73
|
+
mul: ->(a, b) { a * b },
|
|
74
|
+
and: ->(a, b) { a & b },
|
|
75
|
+
or: ->(a, b) { a | b },
|
|
76
|
+
xor: ->(a, b) { a ^ b },
|
|
77
|
+
shl: ->(a, b) { a << b },
|
|
78
|
+
# An arithmetic (sign-extending) right shift, which is exactly what
|
|
79
|
+
# Ruby's Integer#>> already does for an arbitrary-precision value.
|
|
80
|
+
shr: ->(a, b) { a >> b },
|
|
81
|
+
eq: ->(a, b) { a == b ? 1 : 0 },
|
|
82
|
+
ne: ->(a, b) { a == b ? 0 : 1 },
|
|
83
|
+
lt: ->(a, b) { a < b ? 1 : 0 },
|
|
84
|
+
le: ->(a, b) { a <= b ? 1 : 0 },
|
|
85
|
+
gt: ->(a, b) { a > b ? 1 : 0 },
|
|
86
|
+
ge: ->(a, b) { a >= b ? 1 : 0 }
|
|
87
|
+
}.freeze
|
|
88
|
+
|
|
89
|
+
# One subscript step of a __builtin_offsetof designator whose index is not
|
|
90
|
+
# a constant expression: its contribution to the offset is the value of
|
|
91
|
+
# `index` (an expression node the caller lowers) times `scale`, the
|
|
92
|
+
# element's byte size.
|
|
93
|
+
OffsetofTerm = Data.define(:index, :scale)
|
|
94
|
+
|
|
95
|
+
def self.evaluate(node, sizeof_expr: nil, pointer_int: nil)
|
|
96
|
+
new(sizeof_expr: sizeof_expr, pointer_int: pointer_int).evaluate(node)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Splits an AST::BuiltinOffsetof into [constant_offset, terms]: the byte
|
|
100
|
+
# offset every member step and constant-indexed subscript contributes, and
|
|
101
|
+
# an OffsetofTerm for each subscript whose index is not constant. See
|
|
102
|
+
# #offsetof_plan.
|
|
103
|
+
def self.offsetof_plan(node, sizeof_expr: nil, pointer_int: nil)
|
|
104
|
+
new(sizeof_expr: sizeof_expr, pointer_int: pointer_int).offsetof_plan(node)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# `sizeof_expr`, when supplied, resolves a `sizeof <expression>` operand
|
|
108
|
+
# (AST::SizeofExpr) to its byte size. The evaluator itself carries no
|
|
109
|
+
# symbol table, so it cannot infer an expression's type on its own; a
|
|
110
|
+
# caller that can (the IR generator, which knows every declaration's type)
|
|
111
|
+
# passes a resolver here so a static initializer or other constant context
|
|
112
|
+
# can fold "sizeof x". Without one, `sizeof <expression>` stays a
|
|
113
|
+
# non-constant, as it is in a context with no type information (an array
|
|
114
|
+
# bound folded during parsing).
|
|
115
|
+
# `pointer_int`, when supplied, folds a pointer→integer cast whose operand
|
|
116
|
+
# is an address constant of a load-time-known absolute value — the
|
|
117
|
+
# "(size_t)&((T*)0)->member" offsetof idiom, whose value is the member's
|
|
118
|
+
# byte offset. The address-constant machinery lives in the IR generator, so
|
|
119
|
+
# a caller that has it (a static initializer) passes a resolver rather than
|
|
120
|
+
# duplicating it here; without one such a cast stays a non-constant.
|
|
121
|
+
def initialize(sizeof_expr: nil, pointer_int: nil)
|
|
122
|
+
@sizeof_expr = sizeof_expr
|
|
123
|
+
@pointer_int = pointer_int
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def evaluate(node)
|
|
127
|
+
case node
|
|
128
|
+
when AST::IntLit
|
|
129
|
+
node.value
|
|
130
|
+
when AST::Unary
|
|
131
|
+
evaluate_unary(node)
|
|
132
|
+
when AST::Binary
|
|
133
|
+
evaluate_binary(node)
|
|
134
|
+
when AST::LogicalAnd
|
|
135
|
+
evaluate_logical_and(node)
|
|
136
|
+
when AST::LogicalOr
|
|
137
|
+
evaluate_logical_or(node)
|
|
138
|
+
when AST::Conditional
|
|
139
|
+
evaluate(node.condition).zero? ? evaluate(node.else_expr) : evaluate(node.then_expr)
|
|
140
|
+
when AST::Cast
|
|
141
|
+
evaluate_cast(node)
|
|
142
|
+
when AST::SizeofExpr
|
|
143
|
+
evaluate_sizeof_expr(node)
|
|
144
|
+
when AST::SizeofType
|
|
145
|
+
evaluate_sizeof_type(node)
|
|
146
|
+
when AST::AlignofType
|
|
147
|
+
evaluate_alignof_type(node)
|
|
148
|
+
when AST::BuiltinOffsetof
|
|
149
|
+
evaluate_builtin_offsetof(node)
|
|
150
|
+
when AST::BuiltinConstantP
|
|
151
|
+
evaluate_builtin_constant_p(node)
|
|
152
|
+
when AST::BuiltinBitScan
|
|
153
|
+
evaluate_builtin_bit_scan(node)
|
|
154
|
+
else
|
|
155
|
+
# Every other node — VariableRef, Call, Assignment,
|
|
156
|
+
# CompoundAssignment, IncDec, MemberAccess, Subscript, StringLit,
|
|
157
|
+
# Comma — is not a constant-expression here.
|
|
158
|
+
raise NotConstant, node.token
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Walks a __builtin_offsetof designator once and reports it as
|
|
163
|
+
# [constant_offset, terms]: the bytes every member step and
|
|
164
|
+
# constant-indexed subscript contributes, plus one OffsetofTerm per
|
|
165
|
+
# subscript whose index is not a constant expression.
|
|
166
|
+
#
|
|
167
|
+
# gcc's __builtin_offsetof admits such a subscript and computes the offset
|
|
168
|
+
# at run time (measured: "offsetof(struct S, d[n])" with a variable n
|
|
169
|
+
# compiles and returns 8, 16, 32 for n = 0, 1, 3 on an 8-byte element),
|
|
170
|
+
# which ISO offsetof does not, so a caller that can emit code (the IR
|
|
171
|
+
# generator) takes the terms and lowers them while a caller that needs a
|
|
172
|
+
# constant (#evaluate_builtin_offsetof, and through it every array bound,
|
|
173
|
+
# case label and static initializer) rejects them.
|
|
174
|
+
def offsetof_plan(node)
|
|
175
|
+
type = node.type
|
|
176
|
+
offset = 0
|
|
177
|
+
terms = []
|
|
178
|
+
node.designator.each do |step|
|
|
179
|
+
case step
|
|
180
|
+
when AST::OffsetofMember
|
|
181
|
+
offset, type = offsetof_member_step(type, step, offset)
|
|
182
|
+
when AST::OffsetofIndex
|
|
183
|
+
offset, type = offsetof_index_step(type, step, offset, terms)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
[offset, terms]
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
private
|
|
190
|
+
|
|
191
|
+
def evaluate_unary(node)
|
|
192
|
+
case node.op
|
|
193
|
+
when :neg then -evaluate(node.operand)
|
|
194
|
+
when :not then evaluate(node.operand).zero? ? 1 : 0
|
|
195
|
+
else raise NotConstant, node.token # :addr, :deref
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def evaluate_binary(node)
|
|
200
|
+
return evaluate_division(node) if node.op == :div || node.op == :mod
|
|
201
|
+
return evaluate_pointer_subtraction(node) if node.op == :sub
|
|
202
|
+
|
|
203
|
+
operation = BINARY_OPERATIONS[node.op]
|
|
204
|
+
raise NotConstant, node.token unless operation
|
|
205
|
+
|
|
206
|
+
operation.call(evaluate(node.lhs), evaluate(node.rhs))
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Subtraction is ordinary integer arithmetic unless that fails and both
|
|
210
|
+
# operands are addresses this evaluator can place on its own (see
|
|
211
|
+
# #pointer_target) — the traditional "((size_t)(char *)&((t *)0)->m -
|
|
212
|
+
# (char *)0)" offsetof idiom, and its relatives with any pointed-to type,
|
|
213
|
+
# not only "char *". C's pointer-difference rule (6.5.6p9) then applies:
|
|
214
|
+
# the byte difference is divided by the shared pointed-to type's size,
|
|
215
|
+
# which must be identical on both sides and have one ("void *" and an
|
|
216
|
+
# incomplete type do not) for the division to mean anything. When
|
|
217
|
+
# #pointer_difference cannot place the difference this way it returns
|
|
218
|
+
# nil, and the original NotConstant from the ordinary attempt is what
|
|
219
|
+
# reaches the caller instead.
|
|
220
|
+
def evaluate_pointer_subtraction(node)
|
|
221
|
+
BINARY_OPERATIONS[:sub].call(evaluate(node.lhs), evaluate(node.rhs))
|
|
222
|
+
rescue NotConstant
|
|
223
|
+
difference = pointer_difference(node)
|
|
224
|
+
raise if difference.nil?
|
|
225
|
+
|
|
226
|
+
difference
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# The [byte-difference / pointed-to-size] quotient of "lhs - rhs" when
|
|
230
|
+
# both sides are addresses #pointer_target can place, or nil when either
|
|
231
|
+
# side is not one or the two pointed-to types do not share a size. A
|
|
232
|
+
# non-zero remainder never happens for two addresses this evaluator
|
|
233
|
+
# itself derived from the same struct layout, but is still checked
|
|
234
|
+
# rather than silently truncated, surfacing as an (unfolded) NotConstant.
|
|
235
|
+
def pointer_difference(node)
|
|
236
|
+
lhs = pointer_target(node.lhs)
|
|
237
|
+
return nil if lhs.nil?
|
|
238
|
+
|
|
239
|
+
rhs = pointer_target(node.rhs)
|
|
240
|
+
return nil if rhs.nil?
|
|
241
|
+
|
|
242
|
+
lhs_address, lhs_type = lhs
|
|
243
|
+
rhs_address, rhs_type = rhs
|
|
244
|
+
return nil unless sized?(lhs_type) && sized?(rhs_type) && lhs_type.size == rhs_type.size
|
|
245
|
+
|
|
246
|
+
byte_difference = lhs_address - rhs_address
|
|
247
|
+
raise NotConstant, node.token unless (byte_difference % lhs_type.size).zero?
|
|
248
|
+
|
|
249
|
+
byte_difference / lhs_type.size
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Division and remainder truncate toward zero (6.5.5p6), unlike Ruby's
|
|
253
|
+
# own "/" and "%", which floor. -7 / 2 is -3, not -4, and -7 % 2 is -1,
|
|
254
|
+
# not 1.
|
|
255
|
+
def evaluate_division(node)
|
|
256
|
+
lhs = evaluate(node.lhs)
|
|
257
|
+
rhs = evaluate(node.rhs)
|
|
258
|
+
raise DivisionByZero, node.token if rhs.zero?
|
|
259
|
+
|
|
260
|
+
quotient = lhs.abs / rhs.abs
|
|
261
|
+
quotient = -quotient if (lhs.negative?) != (rhs.negative?)
|
|
262
|
+
node.op == :div ? quotient : lhs - (quotient * rhs)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Short-circuits exactly like the run-time operator: the right operand
|
|
266
|
+
# is not evaluated (and so cannot raise DivisionByZero) once the left
|
|
267
|
+
# settles the result on its own.
|
|
268
|
+
def evaluate_logical_and(node)
|
|
269
|
+
return 0 if evaluate(node.lhs).zero?
|
|
270
|
+
|
|
271
|
+
evaluate(node.rhs).zero? ? 0 : 1
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def evaluate_logical_or(node)
|
|
275
|
+
return 1 unless evaluate(node.lhs).zero?
|
|
276
|
+
|
|
277
|
+
evaluate(node.rhs).zero? ? 0 : 1
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# A cast to an integer type wraps the operand's value into that type's
|
|
281
|
+
# width and signedness; any other destination (a pointer, a struct) is
|
|
282
|
+
# not something this evaluator folds. When the operand is itself a
|
|
283
|
+
# floating-point constant (a literal, or a negated literal — a nested
|
|
284
|
+
# cast such as "(long)(double)1e2" is not chased any further), 6.3.1.4p1
|
|
285
|
+
# truncates it toward zero before the wrap; #evaluate never sees a Float
|
|
286
|
+
# for any other node, so the arithmetic above stays purely integral.
|
|
287
|
+
def evaluate_cast(node)
|
|
288
|
+
raise NotConstant, node.token unless node.type.integer?
|
|
289
|
+
|
|
290
|
+
float_value = float_constant_value(node.operand)
|
|
291
|
+
return wrap_to_type(float_value.truncate, node.type) unless float_value.nil?
|
|
292
|
+
|
|
293
|
+
wrap_to_type(evaluate_integer_or_address(node.operand), node.type)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# The integer value of a cast-to-integer operand: an integer constant
|
|
297
|
+
# expression normally, but a pointer→integer cast has a pointer operand the
|
|
298
|
+
# evaluator does not fold as an ordinary expression. One shape it folds
|
|
299
|
+
# itself is the traditional "(size_t)&((T*)0)->m" offsetof idiom, whose
|
|
300
|
+
# address is a member offset from a constant base and so needs nothing but
|
|
301
|
+
# the struct layout the evaluator already reads (see
|
|
302
|
+
# #absolute_pointer_value). When that does not apply and a @pointer_int
|
|
303
|
+
# resolver is supplied, the operand is offered there instead; the resolver
|
|
304
|
+
# returns the pointer's absolute integer value or re-raises NotConstant.
|
|
305
|
+
def evaluate_integer_or_address(operand)
|
|
306
|
+
evaluate(operand)
|
|
307
|
+
rescue NotConstant
|
|
308
|
+
absolute = absolute_pointer_value(operand)
|
|
309
|
+
return absolute unless absolute.nil?
|
|
310
|
+
raise unless @pointer_int
|
|
311
|
+
|
|
312
|
+
@pointer_int.call(operand)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# The absolute integer value of a pointer-valued operand this evaluator can
|
|
316
|
+
# place on its own — "&((T *)N)->m" and its relatives, where the address-of
|
|
317
|
+
# applies to a designator rooted at a pointer cast of an integer constant,
|
|
318
|
+
# or such a cast written on its own. That is the offsetof idiom a header
|
|
319
|
+
# writes as "((size_t)&((T *)0)->m)", whose value is the member's byte
|
|
320
|
+
# offset; folding it here rather than through a resolver makes it a
|
|
321
|
+
# constant in every context, the parser's (a _Static_assert, an enumerator,
|
|
322
|
+
# a bit-field width, an array bound) as much as the generator's. Anything
|
|
323
|
+
# else is nil, an address only known at link time (a global's) included,
|
|
324
|
+
# which the @pointer_int resolver still handles.
|
|
325
|
+
def absolute_pointer_value(node)
|
|
326
|
+
case node
|
|
327
|
+
when AST::Unary
|
|
328
|
+
return nil unless node.op == :addr
|
|
329
|
+
|
|
330
|
+
address, = designator_address(node.operand)
|
|
331
|
+
address
|
|
332
|
+
when AST::Cast
|
|
333
|
+
pointer_target(node)&.first
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# The [absolute address, pointed-to type] a pointer-valued expression
|
|
338
|
+
# denotes when its value is a compile-time constant: a cast to pointer type
|
|
339
|
+
# over an integer constant (or over another such pointer, as
|
|
340
|
+
# "(T *)(void *)0" writes it), or the address of a designator #designator_address
|
|
341
|
+
# can place. An array-typed operand never reaches here — the idiom's base is
|
|
342
|
+
# always a cast — so no array-to-pointer decay is modelled.
|
|
343
|
+
def pointer_target(node)
|
|
344
|
+
case node
|
|
345
|
+
when AST::Cast
|
|
346
|
+
return nil unless node.type.pointer?
|
|
347
|
+
|
|
348
|
+
address = constant_integer(node.operand) || pointer_target(node.operand)&.first
|
|
349
|
+
address.nil? ? nil : [address, node.type.target]
|
|
350
|
+
when AST::Unary
|
|
351
|
+
return nil unless node.op == :addr
|
|
352
|
+
|
|
353
|
+
designator_address(node.operand)
|
|
354
|
+
when AST::Binary
|
|
355
|
+
pointer_offset_target(node)
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
# "pointer + integer" and "pointer - integer" (6.5.6p8) over a pointer
|
|
360
|
+
# #pointer_target already places: the address moves by the integer
|
|
361
|
+
# operand times the pointed-to type's size, on whichever side of "+" the
|
|
362
|
+
# pointer operand is ("p + 1" and "1 + p" are both valid), or only the
|
|
363
|
+
# left side of "-" ("1 - p" is not a pointer expression at all). nil when
|
|
364
|
+
# neither operand is a foldable pointer, the other is not an integer
|
|
365
|
+
# constant, or the pointed-to type has no size to stride by.
|
|
366
|
+
def pointer_offset_target(node)
|
|
367
|
+
return nil unless node.op == :add || node.op == :sub
|
|
368
|
+
|
|
369
|
+
lhs_pointer = pointer_target(node.lhs)
|
|
370
|
+
if lhs_pointer
|
|
371
|
+
offset = constant_integer(node.rhs)
|
|
372
|
+
return nil if offset.nil?
|
|
373
|
+
|
|
374
|
+
return pointer_advance(lhs_pointer, node.op == :sub ? -offset : offset)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
return nil if node.op == :sub
|
|
378
|
+
|
|
379
|
+
rhs_pointer = pointer_target(node.rhs)
|
|
380
|
+
return nil if rhs_pointer.nil?
|
|
381
|
+
|
|
382
|
+
offset = constant_integer(node.lhs)
|
|
383
|
+
return nil if offset.nil?
|
|
384
|
+
|
|
385
|
+
pointer_advance(rhs_pointer, offset)
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# [address + offset * pointed-to size, pointed-to type], or nil when the
|
|
389
|
+
# pointed-to type has no size to stride by (void, a function, an
|
|
390
|
+
# incomplete aggregate or array).
|
|
391
|
+
def pointer_advance(pointer, offset)
|
|
392
|
+
address, type = pointer
|
|
393
|
+
return nil unless sized?(type)
|
|
394
|
+
|
|
395
|
+
[address + (offset * type.size), type]
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
# The [absolute address, type] of a designator whose base is a pointer of
|
|
399
|
+
# constant value: "*(T *)N" is [N, T], a member access adds the member's
|
|
400
|
+
# offset (6.7.2.1's layout, the same one #offsetof_member_step walks) and a
|
|
401
|
+
# subscript adds the index times the element size. nil for every other
|
|
402
|
+
# designator — one rooted at a named object, or at a pointer whose value is
|
|
403
|
+
# not constant.
|
|
404
|
+
#
|
|
405
|
+
# Unlike the __builtin_offsetof steps this mirrors, a step that names no
|
|
406
|
+
# byte offset (an unknown member, a bit-field, a subscript of a non-array)
|
|
407
|
+
# yields nil rather than an OffsetofError: the expression is an ordinary
|
|
408
|
+
# one the surrounding context still type-checks, so leaving it unfolded
|
|
409
|
+
# keeps that context's own diagnostic instead of pre-empting it here.
|
|
410
|
+
def designator_address(node)
|
|
411
|
+
case node
|
|
412
|
+
when AST::Unary
|
|
413
|
+
node.op == :deref ? pointer_target(node.operand) : nil
|
|
414
|
+
when AST::MemberAccess
|
|
415
|
+
member_designator_address(node)
|
|
416
|
+
when AST::Subscript
|
|
417
|
+
subscript_designator_address(node)
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
# "base->m" over a constant pointer, or "base.m" over a designator already
|
|
422
|
+
# placed, adds the member's offset to the base address.
|
|
423
|
+
def member_designator_address(node)
|
|
424
|
+
base = node.arrow ? pointer_target(node.base) : designator_address(node.base)
|
|
425
|
+
return nil if base.nil?
|
|
426
|
+
|
|
427
|
+
address, type = base
|
|
428
|
+
return nil unless type.struct? && type.complete?
|
|
429
|
+
|
|
430
|
+
member = type.member(node.member)
|
|
431
|
+
return nil if member.nil? || member.bitfield?
|
|
432
|
+
|
|
433
|
+
[address + member.offset, member.type]
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# "base[i]" strides by the element size: over a constant pointer ("((T *)N)[i]")
|
|
437
|
+
# the element is what it points to, over an array designator
|
|
438
|
+
# ("((T *)0)->a[i]") it is the array's element type. A non-constant index,
|
|
439
|
+
# or an element with no size, leaves the address unfolded.
|
|
440
|
+
def subscript_designator_address(node)
|
|
441
|
+
index = constant_integer(node.index)
|
|
442
|
+
return nil if index.nil?
|
|
443
|
+
|
|
444
|
+
address, element = subscript_base(node.target)
|
|
445
|
+
return nil if address.nil? || !sized?(element)
|
|
446
|
+
|
|
447
|
+
[address + (index * element.size), element]
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
# Whether `type` has a byte size a subscript can stride by — every type
|
|
451
|
+
# but void, a function, an incomplete aggregate and an unbounded array.
|
|
452
|
+
def sized?(type)
|
|
453
|
+
return false if type.void? || type.function? || incomplete_aggregate?(type)
|
|
454
|
+
|
|
455
|
+
!(type.array? && type.incomplete?)
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
# The [base address, element type] a subscript strides over, or nil when
|
|
459
|
+
# neither form applies.
|
|
460
|
+
def subscript_base(target)
|
|
461
|
+
pointer = pointer_target(target)
|
|
462
|
+
return pointer if pointer
|
|
463
|
+
|
|
464
|
+
address, type = designator_address(target)
|
|
465
|
+
address.nil? || !type.array? ? nil : [address, type.element]
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
# `node`'s value as an integer constant, or nil when it is not one. Used
|
|
469
|
+
# where a non-constant sub-expression only means the surrounding address
|
|
470
|
+
# stays unfolded, rather than being a failure to report.
|
|
471
|
+
def constant_integer(node)
|
|
472
|
+
evaluate(node)
|
|
473
|
+
rescue NotConstant, DivisionByZero
|
|
474
|
+
nil
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
# The Float a floating-point constant operand denotes, or nil when
|
|
478
|
+
# `node` is not one — a plain FloatLit, or a unary minus directly over
|
|
479
|
+
# one.
|
|
480
|
+
def float_constant_value(node)
|
|
481
|
+
case node
|
|
482
|
+
when AST::FloatLit
|
|
483
|
+
node.value
|
|
484
|
+
when AST::Unary
|
|
485
|
+
return nil unless node.op == :neg && node.operand.is_a?(AST::FloatLit)
|
|
486
|
+
|
|
487
|
+
-node.operand.value
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
def wrap_to_type(value, type)
|
|
492
|
+
return value.zero? ? 0 : 1 if type.bool?
|
|
493
|
+
|
|
494
|
+
bits = type.size * 8
|
|
495
|
+
wrapped = value & ((1 << bits) - 1)
|
|
496
|
+
return wrapped if type.unsigned? || wrapped < (1 << (bits - 1))
|
|
497
|
+
|
|
498
|
+
wrapped - (1 << bits)
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
# sizeof(expression) folds to the byte size of the operand's type, which
|
|
502
|
+
# only a caller carrying type information can supply (see #initialize): the
|
|
503
|
+
# resolver returns the size, applying the same "no size" rejections
|
|
504
|
+
# sizeof(type-name) does. Without a resolver it is a non-constant, so a
|
|
505
|
+
# context with no type table (an array bound folded while parsing) reports
|
|
506
|
+
# "not an integer constant" rather than a wrong value.
|
|
507
|
+
def evaluate_sizeof_expr(node)
|
|
508
|
+
raise NotConstant, node.token unless @sizeof_expr
|
|
509
|
+
|
|
510
|
+
@sizeof_expr.call(node)
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
# sizeof(type-name) folds to the type's byte size; an incomplete type
|
|
514
|
+
# (void, a struct/union never completed, or a forward-referenced enum) has
|
|
515
|
+
# none to fold.
|
|
516
|
+
def evaluate_sizeof_type(node)
|
|
517
|
+
type = node.type
|
|
518
|
+
raise NotConstant, node.token if type.void? || incomplete_aggregate?(type)
|
|
519
|
+
|
|
520
|
+
type.size
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
# _Alignof(type-name) folds to the type's alignment; a void, function or
|
|
524
|
+
# incomplete type has no alignment to fold, matching what the generator
|
|
525
|
+
# rejects for the same construct.
|
|
526
|
+
def evaluate_alignof_type(node)
|
|
527
|
+
type = node.type
|
|
528
|
+
if type.void? || type.function? || incomplete_aggregate?(type)
|
|
529
|
+
raise NotConstant, node.token
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
type.alignment
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
# __builtin_offsetof(type-name, member-designator) folds to the byte offset
|
|
536
|
+
# of the designated member, walking the designator one step at a time from
|
|
537
|
+
# the aggregate type. A member step adds the member's offset and descends
|
|
538
|
+
# into its type; a subscript step (over an array member) adds the index
|
|
539
|
+
# times the element size and descends into the element type. Every failure
|
|
540
|
+
# — a non-aggregate or incomplete type, a missing member, a subscript of a
|
|
541
|
+
# non-array, or a bit-field target with no addressable offset — is an
|
|
542
|
+
# OffsetofError carrying the diagnostic wording. The member lookup goes
|
|
543
|
+
# through Type::StructType#member, so an anonymous struct/union member is
|
|
544
|
+
# traversed transparently with its own offset already folded in.
|
|
545
|
+
def evaluate_builtin_offsetof(node)
|
|
546
|
+
offset, terms = offsetof_plan(node)
|
|
547
|
+
# A constant context has no place to evaluate a run-time index, so a
|
|
548
|
+
# deferred term is simply the non-constant expression it came from.
|
|
549
|
+
raise NotConstant, terms.first.index.token unless terms.empty?
|
|
550
|
+
|
|
551
|
+
offset
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
def offsetof_member_step(type, step, offset)
|
|
555
|
+
unless type.struct?
|
|
556
|
+
raise OffsetofError.new(step.token,
|
|
557
|
+
"request for member '#{step.name}' in something not a structure or union")
|
|
558
|
+
end
|
|
559
|
+
unless type.complete?
|
|
560
|
+
raise OffsetofError.new(step.token, "offsetof of incomplete type '#{type}'")
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
member = type.member(step.name)
|
|
564
|
+
raise OffsetofError.new(step.token, "no member named '#{step.name}' in '#{type}'") if member.nil?
|
|
565
|
+
if member.bitfield?
|
|
566
|
+
raise OffsetofError.new(step.token, "attempt to get the offset of a bit-field member '#{step.name}'")
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
[offset + member.offset, member.type]
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
# One "[i]" step. A constant index folds straight into the running offset;
|
|
573
|
+
# a non-constant one is deferred to `terms` for a caller that can lower it
|
|
574
|
+
# (an OffsetofError from a nested designator is a real diagnostic, not a
|
|
575
|
+
# deferrable index, so it passes through).
|
|
576
|
+
def offsetof_index_step(type, step, offset, terms)
|
|
577
|
+
unless type.array?
|
|
578
|
+
raise OffsetofError.new(step.token, "subscripted value in offsetof is not an array")
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
element = type.element
|
|
582
|
+
begin
|
|
583
|
+
[offset + evaluate(step.index) * element.size, element]
|
|
584
|
+
rescue OffsetofError
|
|
585
|
+
raise
|
|
586
|
+
rescue NotConstant
|
|
587
|
+
terms << OffsetofTerm.new(index: step.index, scale: element.size)
|
|
588
|
+
[offset, element]
|
|
589
|
+
end
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
# __builtin_constant_p(expr) folds to 1 when its operand is itself a
|
|
593
|
+
# constant-expression and 0 otherwise. The operand is probed by trying to
|
|
594
|
+
# evaluate it: success means it reduced to a constant (so 1), while a
|
|
595
|
+
# NotConstant (a variable, call, ...) or a DivisionByZero means it did not
|
|
596
|
+
# (so 0). It never propagates the failure — unlike an ordinary
|
|
597
|
+
# sub-expression, a non-constant operand here is a legitimate 0, not an
|
|
598
|
+
# error — so the probe swallows both. The operand is evaluated only to test
|
|
599
|
+
# foldability; the caller discards the value.
|
|
600
|
+
def evaluate_builtin_constant_p(node)
|
|
601
|
+
evaluate(node.expr)
|
|
602
|
+
1
|
|
603
|
+
rescue NotConstant, DivisionByZero
|
|
604
|
+
0
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
# __builtin_ctz/clz(x) folds to the trailing/leading zero-bit count of a
|
|
608
|
+
# constant operand, over its `width`-byte value, matching gcc so the same
|
|
609
|
+
# fold serves a HAVE_BUILTIN___BUILTIN_CLZLL-guarded constant context. A
|
|
610
|
+
# zero operand is undefined behavior (gcc), so it is left unfolded (a
|
|
611
|
+
# NotConstant) rather than given an arbitrary value.
|
|
612
|
+
def evaluate_builtin_bit_scan(node)
|
|
613
|
+
bits = node.width * 8
|
|
614
|
+
value = evaluate(node.operand) & ((1 << bits) - 1)
|
|
615
|
+
raise NotConstant, node.token if value.zero?
|
|
616
|
+
|
|
617
|
+
if node.direction == :forward
|
|
618
|
+
(value & -value).bit_length - 1 # trailing zero count
|
|
619
|
+
else
|
|
620
|
+
bits - value.bit_length # leading zero count
|
|
621
|
+
end
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
# Whether `type` is an incomplete tagged type with no size or alignment: a
|
|
625
|
+
# struct/union never completed, or an incomplete (forward-referenced) enum.
|
|
626
|
+
def incomplete_aggregate?(type)
|
|
627
|
+
(type.struct? && !type.complete?) || type.is_a?(Rubycc::Type::EnumType)
|
|
628
|
+
end
|
|
629
|
+
end
|
|
630
|
+
end
|
|
631
|
+
end
|