rubycc 1.0.0 → 1.1.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 +4 -4
- data/CHANGELOG.md +61 -0
- data/README.md +26 -14
- data/data/verified_gems.json +85 -63
- data/exe/rubycc-ar +11 -3
- data/include/libc/sys/cdefs.h +12 -0
- data/lib/rubycc/backend/aarch64.rb +705 -117
- data/lib/rubycc/backend/slot_residency.rb +169 -0
- data/lib/rubycc/backend/x86_64.rb +924 -137
- data/lib/rubycc/command_line.rb +339 -0
- data/lib/rubycc/compile_error.rb +6 -3
- data/lib/rubycc/compiler.rb +17 -2
- data/lib/rubycc/diagnostics.rb +105 -0
- data/lib/rubycc/doctor/gemfile.rb +12 -3
- data/lib/rubycc/doctor/verified_gems.rb +5 -1
- data/lib/rubycc/driver.rb +66 -10
- data/lib/rubycc/front/ast.rb +18 -7
- data/lib/rubycc/front/constant_evaluator.rb +12 -0
- data/lib/rubycc/front/lexeme_reader.rb +3 -1
- data/lib/rubycc/front/parser.rb +51 -18
- data/lib/rubycc/ir/analysis.rb +82 -0
- data/lib/rubycc/ir/call_convention.rb +74 -7
- data/lib/rubycc/ir/generator.rb +319 -9
- data/lib/rubycc/ir/ir.rb +39 -1
- data/lib/rubycc/ir/promotion.rb +255 -0
- data/lib/rubycc/ir/simplify.rb +570 -0
- data/lib/rubycc/link/library_resolver.rb +17 -5
- data/lib/rubycc/link/partial_linker.rb +8 -1
- data/lib/rubycc/link/shared_linker.rb +2 -2
- data/lib/rubycc/mkmf_shim.rb +178 -12
- data/lib/rubycc/objfile/ar_archive.rb +13 -2
- data/lib/rubycc/objfile/elf_reader.rb +13 -2
- data/lib/rubycc/pkgconf/parser.rb +4 -0
- data/lib/rubycc/pkgconf/resolver.rb +3 -1
- data/lib/rubycc/pkgconf/system_path_filter.rb +8 -2
- data/lib/rubycc/preprocess/preprocessor.rb +161 -37
- data/lib/rubycc/preprocess/scanner.rb +69 -14
- data/lib/rubycc/preprocess/token_converter.rb +11 -1
- data/lib/rubycc/rmake/cli.rb +32 -4
- data/lib/rubycc/rmake/executor.rb +157 -226
- data/lib/rubycc/rmake/makefile.rb +35 -13
- data/lib/rubycc/rmake/parser.rb +8 -2
- data/lib/rubycc/rmake/rmake.rb +1 -0
- data/lib/rubycc/rmake/tool_command.rb +69 -0
- data/lib/rubycc/shell.rb +510 -0
- data/lib/rubycc/type.rb +23 -7
- data/lib/rubycc/version.rb +1 -1
- data/lib/rubycc.rb +12 -0
- data/lib/rubygems_plugin.rb +31 -3
- metadata +14 -3
data/lib/rubycc/driver.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "rbconfig"
|
|
4
|
+
require_relative "diagnostics"
|
|
4
5
|
require_relative "compile_error"
|
|
5
6
|
require_relative "compiler"
|
|
6
7
|
require_relative "preprocess/preprocessor"
|
|
@@ -67,7 +68,12 @@ module Rubycc
|
|
|
67
68
|
end
|
|
68
69
|
|
|
69
70
|
def initialize(argv, stdout: $stdout, stderr: $stderr)
|
|
70
|
-
|
|
71
|
+
# The command line crosses into rubycc here, so it is re-tagged as bytes
|
|
72
|
+
# (lib/rubycc.rb): every option is spelled in ASCII and every operand is a
|
|
73
|
+
# path or a macro spelling, none of which this driver reads as text. The
|
|
74
|
+
# array is rebuilt rather than mutated, since an in-process caller (rmake's
|
|
75
|
+
# executor) owns the one it passes.
|
|
76
|
+
@argv = argv.map { |arg| arg.encoding == Encoding::BINARY ? arg : arg.b }
|
|
71
77
|
@out = stdout
|
|
72
78
|
@err = stderr
|
|
73
79
|
@inputs = [] # [{ path:, kind: }] in command-line order
|
|
@@ -83,13 +89,18 @@ module Rubycc
|
|
|
83
89
|
@system_includes = true # -nostdinc clears this
|
|
84
90
|
@default_libs = true # -nodefaultlibs clears this
|
|
85
91
|
@target = nil # -target/--target; defaults to the host CPU
|
|
92
|
+
@warnings = true # -w clears this
|
|
86
93
|
end
|
|
87
94
|
|
|
88
95
|
def run
|
|
89
96
|
return print_version if version_requested?
|
|
90
97
|
|
|
91
98
|
parse
|
|
92
|
-
|
|
99
|
+
# Compiler warnings (Diagnostics.warn, today only #warning) follow this
|
|
100
|
+
# invocation's error stream, so an in-process caller that injected one —
|
|
101
|
+
# rmake running the Driver inside a step worker — captures them with
|
|
102
|
+
# everything else this run reports. `-w` discards them instead.
|
|
103
|
+
Diagnostics.to(@warnings ? @err : nil) { dispatch }
|
|
93
104
|
0
|
|
94
105
|
rescue UsageError => e
|
|
95
106
|
@err.puts "#{PROG}: error: #{e.message}"
|
|
@@ -145,6 +156,7 @@ module Rubycc
|
|
|
145
156
|
when "-fvisibility=default" then @default_visibility = :default; i + 1
|
|
146
157
|
when "-fvisibility=internal" then @default_visibility = :internal; i + 1
|
|
147
158
|
when "-fvisibility=protected" then @default_visibility = :protected; i + 1
|
|
159
|
+
when "-w" then @warnings = false; i + 1
|
|
148
160
|
when "-nostdinc" then @system_includes = false; i + 1
|
|
149
161
|
when "-nodefaultlibs" then @default_libs = false; i + 1
|
|
150
162
|
when "-target", "--target" then @target = normalize_target(value(arg, i)); i + 2
|
|
@@ -186,9 +198,35 @@ module Rubycc
|
|
|
186
198
|
# Whether `arg` is a documented gcc flag family this toolchain does not model
|
|
187
199
|
# but accepts without complaint: the optimization (`-O*`), debug (`-g*`),
|
|
188
200
|
# warning (`-W*`) and remaining code-generation (`-f*`) switches, the language-standard
|
|
189
|
-
# selector (`-std=…`), and the fixed bare set above.
|
|
190
|
-
#
|
|
191
|
-
#
|
|
201
|
+
# selector (`-std=…`), and the fixed bare set above.
|
|
202
|
+
#
|
|
203
|
+
# The warning switches deserve their decision spelled out, now that there is
|
|
204
|
+
# a warning to switch (Diagnostics.warn, reached today only by `#warning`):
|
|
205
|
+
#
|
|
206
|
+
# * `-w` (suppress all warnings) **is honored** — see #handle_arg. It costs
|
|
207
|
+
# one flag and cannot change what compiles, only what is printed, and it
|
|
208
|
+
# was not even in the ignore list before (lowercase, so the `-W*` family
|
|
209
|
+
# regex misses it): a build passing `-w` drew "unknown option '-w'
|
|
210
|
+
# ignored", which is itself noise of the kind `-w` asks to be spared.
|
|
211
|
+
# It silences the compiler's diagnostics only, not this driver's own
|
|
212
|
+
# "PROG: warning: …" lines, which is where gcc draws the same line
|
|
213
|
+
# (measured 2026-08-19, gcc 13.3.0: `gcc -w -c a.c b.o` prints no #warning
|
|
214
|
+
# but still prints "gcc: warning: b.o: linker input file unused …").
|
|
215
|
+
# * `-Werror` (and `-Werror=…`) **is not honored**: it stays accepted and
|
|
216
|
+
# ignored. Honoring it would mean failing a build over the one warning
|
|
217
|
+
# rubycc happens to implement while staying silent about the hundreds gcc
|
|
218
|
+
# would have raised on the same source — a promotion of an arbitrary
|
|
219
|
+
# subset, which is worse than not promoting at all. It would also aim
|
|
220
|
+
# straight at what this channel was built for: a portability header's
|
|
221
|
+
# `#warning "unrecognized compiler"` would go back to killing the build,
|
|
222
|
+
# the very failure #warning support exists to end. Revisit if rubycc ever
|
|
223
|
+
# grows a warning set worth calling a set.
|
|
224
|
+
# * The individual `-Wfoo` / `-Wno-foo` selectors are likewise accepted and
|
|
225
|
+
# ignored: rubycc has no warning categories to select between yet.
|
|
226
|
+
#
|
|
227
|
+
# A machine switch (`-m*`) is deliberately excluded — it names a target
|
|
228
|
+
# capability this toolchain does not honor, so it is warned about like any
|
|
229
|
+
# other unmodelled option.
|
|
192
230
|
def silently_ignored?(arg)
|
|
193
231
|
SILENT_IGNORE_EXACT.include?(arg) ||
|
|
194
232
|
arg.match?(/\A-(?:O|g|W|f)/) || arg.start_with?("-std=")
|
|
@@ -389,7 +427,8 @@ module Rubycc
|
|
|
389
427
|
end
|
|
390
428
|
|
|
391
429
|
def compile_source(input)
|
|
392
|
-
|
|
430
|
+
# Bytes, not locale-encoded text: see Preprocess::Scanner's class comment.
|
|
431
|
+
Compiler.new.compile(File.binread(input[:path]), filename: input[:path],
|
|
393
432
|
include_paths: @include_paths, pic: @pic, defines: @defines,
|
|
394
433
|
system_includes: @system_includes, target: target, libc: libc,
|
|
395
434
|
default_visibility: @default_visibility)
|
|
@@ -404,12 +443,12 @@ module Rubycc
|
|
|
404
443
|
# byte-faithful copy of gcc's `-E` output; it is enough for a probe that only
|
|
405
444
|
# needs macros expanded and headers included. Non-source inputs are ignored.
|
|
406
445
|
def preprocess_only
|
|
407
|
-
text = +""
|
|
446
|
+
text = +"".b
|
|
408
447
|
@inputs.each do |input|
|
|
409
448
|
next unless input[:kind] == :source
|
|
410
449
|
|
|
411
450
|
tokens = preprocessor_for_target.preprocess(
|
|
412
|
-
File.
|
|
451
|
+
File.binread(input[:path]), filename: input[:path],
|
|
413
452
|
include_paths: @include_paths, defines: @defines,
|
|
414
453
|
system_includes: @system_includes
|
|
415
454
|
)
|
|
@@ -437,7 +476,7 @@ module Rubycc
|
|
|
437
476
|
end
|
|
438
477
|
|
|
439
478
|
def render_preprocessed(tokens)
|
|
440
|
-
out = +""
|
|
479
|
+
out = +"".b
|
|
441
480
|
previous = nil
|
|
442
481
|
tokens.each do |token|
|
|
443
482
|
if previous.nil?
|
|
@@ -447,13 +486,30 @@ module Rubycc
|
|
|
447
486
|
elsif token.space_before
|
|
448
487
|
out << " "
|
|
449
488
|
end
|
|
450
|
-
out << token.text
|
|
489
|
+
out << token_bytes(token.text)
|
|
451
490
|
previous = token
|
|
452
491
|
end
|
|
453
492
|
out << "\n" unless out.empty?
|
|
454
493
|
out
|
|
455
494
|
end
|
|
456
495
|
|
|
496
|
+
# A token's spelling as bytes. Almost every token comes from the scanner and
|
|
497
|
+
# is ASCII-8BIT already (see Preprocess::Scanner); the exceptions are the
|
|
498
|
+
# ones the preprocessor synthesizes rather than scans, and __FILE__ is the
|
|
499
|
+
# one that matters — it expands to a string literal spelled with the file
|
|
500
|
+
# name this driver was handed, which carries the locale's encoding. Appending
|
|
501
|
+
# such a string to a buffer that already holds raw bytes (a UTF-8 string
|
|
502
|
+
# literal out of the source) is an Encoding::CompatibilityError: -E would
|
|
503
|
+
# abort with a Ruby backtrace instead of printing the text it was asked for.
|
|
504
|
+
# Re-tagging is not transcoding — the bytes are the same either way — and it
|
|
505
|
+
# costs an allocation only for the strings that are actually mixed, since a
|
|
506
|
+
# spelling that is already bytes, or is pure ASCII, is compatible as it is.
|
|
507
|
+
def token_bytes(text)
|
|
508
|
+
return text if text.encoding == Encoding::BINARY || text.ascii_only?
|
|
509
|
+
|
|
510
|
+
text.b
|
|
511
|
+
end
|
|
512
|
+
|
|
457
513
|
# --- diagnostics -------------------------------------------------------
|
|
458
514
|
|
|
459
515
|
def warning(message)
|
data/lib/rubycc/front/ast.rb
CHANGED
|
@@ -12,9 +12,11 @@ module Rubycc
|
|
|
12
12
|
IntLit = Data.define(:value, :token, :type)
|
|
13
13
|
|
|
14
14
|
# Floating-point literal. `value` is a Ruby Float and `type` its
|
|
15
|
-
# Rubycc::Type — Type::Float for an f/F-suffixed constant,
|
|
16
|
-
#
|
|
17
|
-
# constant's suffix
|
|
15
|
+
# Rubycc::Type — Type::Float for an f/F-suffixed constant,
|
|
16
|
+
# Type::LongDouble for an l/L-suffixed one and Type::Double for an
|
|
17
|
+
# unsuffixed one — fixed by the parser from the constant's suffix
|
|
18
|
+
# (6.4.4.2). `value` is a double in every case: Type::LongDouble names a
|
|
19
|
+
# double's representation under a different type name (see Type).
|
|
18
20
|
FloatLit = Data.define(:value, :type, :token)
|
|
19
21
|
|
|
20
22
|
# String literal. `value` is the escape-resolved bytes as an ASCII-8BIT
|
|
@@ -394,14 +396,23 @@ module Rubycc
|
|
|
394
396
|
# produces no code and no side effects.
|
|
395
397
|
BuiltinConstantP = Data.define(:expr, :token)
|
|
396
398
|
|
|
397
|
-
# "__builtin_ctz/ctzll/clz/clzll ( x )": counts the trailing
|
|
398
|
-
# leading (clz) zero bits of an integer, typed int. `operand` is
|
|
399
|
-
# scanned, `direction` is :forward for ctz or :reverse for clz,
|
|
400
|
-
# is the operand's byte width (4 for the plain form, 8 for the
|
|
399
|
+
# "__builtin_ctz/ctzl/ctzll/clz/clzl/clzll ( x )": counts the trailing
|
|
400
|
+
# (ctz) or leading (clz) zero bits of an integer, typed int. `operand` is
|
|
401
|
+
# the value scanned, `direction` is :forward for ctz or :reverse for clz,
|
|
402
|
+
# and `width` is the operand's byte width (4 for the plain form, 8 for the
|
|
403
|
+
# "l" and "ll" ones — `long` and `long long` are both 8 bytes here).
|
|
401
404
|
# `x == 0` is undefined behavior (gcc), so no zero handling is implied.
|
|
402
405
|
# `token` is the builtin keyword.
|
|
403
406
|
BuiltinBitScan = Data.define(:operand, :direction, :width, :token)
|
|
404
407
|
|
|
408
|
+
# "__builtin_popcount/popcountl/popcountll ( x )": counts the set bits of
|
|
409
|
+
# an integer, typed int. `operand` is the value counted and `width` its
|
|
410
|
+
# byte width (4 for the plain form, 8 for the "l" and "ll" ones, `long`
|
|
411
|
+
# and `long long` being the same width on both targets). Unlike a bit
|
|
412
|
+
# scan this is total: a zero operand counts zero bits, as in gcc. `token`
|
|
413
|
+
# is the builtin keyword.
|
|
414
|
+
BuiltinPopcount = Data.define(:operand, :width, :token)
|
|
415
|
+
|
|
405
416
|
# "__builtin_add/sub/mul_overflow ( a , b , res )": computes "a op b" with
|
|
406
417
|
# infinite precision, stores the result converted to the type `res` points
|
|
407
418
|
# at (wrapping or truncating like any integer conversion), and yields int 1
|
|
@@ -151,6 +151,8 @@ module Rubycc
|
|
|
151
151
|
evaluate_builtin_constant_p(node)
|
|
152
152
|
when AST::BuiltinBitScan
|
|
153
153
|
evaluate_builtin_bit_scan(node)
|
|
154
|
+
when AST::BuiltinPopcount
|
|
155
|
+
evaluate_builtin_popcount(node)
|
|
154
156
|
else
|
|
155
157
|
# Every other node — VariableRef, Call, Assignment,
|
|
156
158
|
# CompoundAssignment, IncDec, MemberAccess, Subscript, StringLit,
|
|
@@ -621,6 +623,16 @@ module Rubycc
|
|
|
621
623
|
end
|
|
622
624
|
end
|
|
623
625
|
|
|
626
|
+
# __builtin_popcount(x) folds to the number of set bits in a constant
|
|
627
|
+
# operand, over its `width`-byte value — the same mask a bit scan applies,
|
|
628
|
+
# which is what makes __builtin_popcount(-1) the 32 gcc gives (the operand
|
|
629
|
+
# is counted as an unsigned int) rather than an infinite run of sign bits.
|
|
630
|
+
# There is no undefined case to leave unfolded: zero counts zero bits.
|
|
631
|
+
def evaluate_builtin_popcount(node)
|
|
632
|
+
value = evaluate(node.operand) & ((1 << (node.width * 8)) - 1)
|
|
633
|
+
value.to_s(2).count("1")
|
|
634
|
+
end
|
|
635
|
+
|
|
624
636
|
# Whether `type` is an incomplete tagged type with no size or alignment: a
|
|
625
637
|
# struct/union never completed, or an incomplete (forward-referenced) enum.
|
|
626
638
|
def incomplete_aggregate?(type)
|
|
@@ -39,7 +39,9 @@ module Rubycc
|
|
|
39
39
|
__builtin_va_start __builtin_va_arg __builtin_va_end __builtin_va_copy
|
|
40
40
|
__builtin_expect __builtin_alloca __builtin_offsetof
|
|
41
41
|
__builtin_constant_p __builtin_choose_expr
|
|
42
|
-
__builtin_ctz __builtin_ctzll
|
|
42
|
+
__builtin_ctz __builtin_ctzl __builtin_ctzll
|
|
43
|
+
__builtin_clz __builtin_clzl __builtin_clzll
|
|
44
|
+
__builtin_popcount __builtin_popcountl __builtin_popcountll
|
|
43
45
|
__builtin_unreachable __builtin_memcpy
|
|
44
46
|
__builtin_add_overflow __builtin_sub_overflow __builtin_mul_overflow
|
|
45
47
|
__atomic_load_n __atomic_store_n __atomic_exchange_n
|
data/lib/rubycc/front/parser.rb
CHANGED
|
@@ -739,8 +739,9 @@ module Rubycc
|
|
|
739
739
|
# left out — type `int`, located at the identifier itself (6.9.1p6). C90
|
|
740
740
|
# spelled that default out; C11 instead requires every identifier to be
|
|
741
741
|
# declared, but gcc keeps accepting the omission with a -Wimplicit-int
|
|
742
|
-
# warning (measured), and
|
|
743
|
-
#
|
|
742
|
+
# warning (measured), and this compiler warns about nothing in the front
|
|
743
|
+
# end (Diagnostics.warn carries only #warning so far), so the older,
|
|
744
|
+
# permissive reading is the one implemented.
|
|
744
745
|
def old_style_parameters(identifier_list, declared)
|
|
745
746
|
identifier_list.names.map do |name_tok|
|
|
746
747
|
type, const, decl_tok = declared[name_tok.value]
|
|
@@ -865,9 +866,8 @@ module Rubycc
|
|
|
865
866
|
else
|
|
866
867
|
# An init attribute on an object has nothing to register — there is no
|
|
867
868
|
# function to call — so it is refused here. gcc only warns ("attribute
|
|
868
|
-
# ignored"), but
|
|
869
|
-
#
|
|
870
|
-
# misbehaves at run time.
|
|
869
|
+
# ignored"), but this parser emits no warnings, and a silently dropped
|
|
870
|
+
# initializer is invisible until the program misbehaves at run time.
|
|
871
871
|
reject_init_attributes(attributes)
|
|
872
872
|
register_visibility_attributes(name_tok.value, attributes)
|
|
873
873
|
parse_global_declarator(type, name_tok, pointer_quals, spec_info)
|
|
@@ -1282,15 +1282,19 @@ module Rubycc
|
|
|
1282
1282
|
return normalize_standalone(Type::Void, "void", specs, tok) if counts["void"].positive?
|
|
1283
1283
|
return normalize_standalone(Type::Bool, "_Bool", specs, tok) if counts["_Bool"].positive?
|
|
1284
1284
|
return normalize_standalone(Type::Float, "float", specs, tok) if counts["float"].positive?
|
|
1285
|
-
# `double` stands alone or pairs with a single `long` ("long double"
|
|
1286
|
-
#
|
|
1287
|
-
#
|
|
1285
|
+
# `double` stands alone or pairs with a single `long` ("long double");
|
|
1286
|
+
# any other keyword, a second `double` or a second `long` is an
|
|
1287
|
+
# ill-formed combination. The two are separate types here even though
|
|
1288
|
+
# they share a representation: a `long double` handed to a variadic
|
|
1289
|
+
# function must be converted to the target's own long-double format, and
|
|
1290
|
+
# the only way the generator can know to do that is for the name to
|
|
1291
|
+
# survive the declaration (see Type::LongDouble).
|
|
1288
1292
|
if counts["double"].positive?
|
|
1289
1293
|
non_long = specs.reject { |s| s == "long" }
|
|
1290
1294
|
unless non_long == ["double"] && counts["long"] <= 1
|
|
1291
1295
|
error_at(tok, "cannot combine 'double' with other type specifiers")
|
|
1292
1296
|
end
|
|
1293
|
-
return Type::Double
|
|
1297
|
+
return counts["long"].positive? ? Type::LongDouble : Type::Double
|
|
1294
1298
|
end
|
|
1295
1299
|
|
|
1296
1300
|
# `__int128` (a GNU keyword) stands alone or pairs with a single
|
|
@@ -1552,7 +1556,7 @@ module Rubycc
|
|
|
1552
1556
|
# The run-order number of a constructor/destructor. The window and its
|
|
1553
1557
|
# wording follow gcc's measured behavior (see MAX_INIT_PRIORITY); the
|
|
1554
1558
|
# 0..100 range gcc reserves for the implementation is accepted here, since
|
|
1555
|
-
# gcc only warns about it and this
|
|
1559
|
+
# gcc only warns about it and this parser emits no warnings.
|
|
1556
1560
|
def parse_init_priority_argument(name)
|
|
1557
1561
|
expr = parse_parenthesized_constant
|
|
1558
1562
|
value = evaluate_constant_expression(expr, "'#{name}' attribute argument is not an integer constant",
|
|
@@ -3407,12 +3411,16 @@ module Rubycc
|
|
|
3407
3411
|
parse_builtin_choose_expr
|
|
3408
3412
|
elsif peek.keyword?("__builtin_ctz")
|
|
3409
3413
|
parse_builtin_bit_scan(:forward, 4)
|
|
3410
|
-
elsif peek.keyword?("__builtin_ctzll")
|
|
3414
|
+
elsif peek.keyword?("__builtin_ctzl") || peek.keyword?("__builtin_ctzll")
|
|
3411
3415
|
parse_builtin_bit_scan(:forward, 8)
|
|
3412
3416
|
elsif peek.keyword?("__builtin_clz")
|
|
3413
3417
|
parse_builtin_bit_scan(:reverse, 4)
|
|
3414
|
-
elsif peek.keyword?("__builtin_clzll")
|
|
3418
|
+
elsif peek.keyword?("__builtin_clzl") || peek.keyword?("__builtin_clzll")
|
|
3415
3419
|
parse_builtin_bit_scan(:reverse, 8)
|
|
3420
|
+
elsif peek.keyword?("__builtin_popcount")
|
|
3421
|
+
parse_builtin_popcount(4)
|
|
3422
|
+
elsif peek.keyword?("__builtin_popcountl") || peek.keyword?("__builtin_popcountll")
|
|
3423
|
+
parse_builtin_popcount(8)
|
|
3416
3424
|
elsif peek.keyword?("__builtin_unreachable")
|
|
3417
3425
|
parse_builtin_unreachable
|
|
3418
3426
|
elsif peek.keyword?("__builtin_memcpy")
|
|
@@ -3631,10 +3639,15 @@ module Rubycc
|
|
|
3631
3639
|
selector.zero? ? when_false : when_true
|
|
3632
3640
|
end
|
|
3633
3641
|
|
|
3634
|
-
# "__builtin_ctz/
|
|
3635
|
-
# operand whose trailing (`:forward`) or leading
|
|
3636
|
-
# counted over `width` bytes. The generator
|
|
3637
|
-
# type check and lowers the bit scan; the
|
|
3642
|
+
# "__builtin_ctz/clz ( assignment-expression )", in any of their three
|
|
3643
|
+
# spellings: one integer operand whose trailing (`:forward`) or leading
|
|
3644
|
+
# (`:reverse`) zero bits are counted over `width` bytes. The generator
|
|
3645
|
+
# settles the operand's integer type check and lowers the bit scan; the
|
|
3646
|
+
# result is int.
|
|
3647
|
+
#
|
|
3648
|
+
# The "l" spelling shares the "ll" one's width because `long` and `long
|
|
3649
|
+
# long` are both 8 bytes on every target rubycc emits for; a target where
|
|
3650
|
+
# they differed would have to split them here.
|
|
3638
3651
|
def parse_builtin_bit_scan(direction, width)
|
|
3639
3652
|
keyword_tok = advance # the "__builtin_ctz"/... keyword
|
|
3640
3653
|
expect_punct("(")
|
|
@@ -3643,6 +3656,18 @@ module Rubycc
|
|
|
3643
3656
|
AST::BuiltinBitScan.new(operand, direction, width, keyword_tok)
|
|
3644
3657
|
end
|
|
3645
3658
|
|
|
3659
|
+
# "__builtin_popcount/popcountl/popcountll ( assignment-expression )": one
|
|
3660
|
+
# integer operand whose set bits are counted over `width` bytes, the width
|
|
3661
|
+
# coming from the spelling exactly as it does for a bit scan. The generator
|
|
3662
|
+
# checks the operand's type and lowers the count; the result is int.
|
|
3663
|
+
def parse_builtin_popcount(width)
|
|
3664
|
+
keyword_tok = advance # the "__builtin_popcount"/... keyword
|
|
3665
|
+
expect_punct("(")
|
|
3666
|
+
operand = parse_assignment_expression
|
|
3667
|
+
expect_punct(")")
|
|
3668
|
+
AST::BuiltinPopcount.new(operand, width, keyword_tok)
|
|
3669
|
+
end
|
|
3670
|
+
|
|
3646
3671
|
# "__builtin_add/sub/mul_overflow ( a , b , res )": exactly three arguments,
|
|
3647
3672
|
# parsed as an ordinary argument list so a wrong count is an arity
|
|
3648
3673
|
# diagnostic. The keyword decides only the operator; the operand types are
|
|
@@ -3837,8 +3862,16 @@ module Rubycc
|
|
|
3837
3862
|
elsif tok.type == :float
|
|
3838
3863
|
advance
|
|
3839
3864
|
# The suffix (from the lexer) fixes the constant's type: "f"/"F" is
|
|
3840
|
-
# float,
|
|
3841
|
-
|
|
3865
|
+
# float, "l"/"L" long double, and no suffix double. A long-double
|
|
3866
|
+
# constant carries only a double's value and precision (that is what
|
|
3867
|
+
# Type::LongDouble models), but it must keep the name so that
|
|
3868
|
+
# "printf(\"%Lg\", 1.5L)" passes the argument in the long-double form
|
|
3869
|
+
# the callee reads.
|
|
3870
|
+
type = case tok.suffix
|
|
3871
|
+
when "f" then Type::Float
|
|
3872
|
+
when "l" then Type::LongDouble
|
|
3873
|
+
else Type::Double
|
|
3874
|
+
end
|
|
3842
3875
|
AST::FloatLit.new(tok.value, type, tok)
|
|
3843
3876
|
elsif tok.type == :string
|
|
3844
3877
|
advance
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "ir"
|
|
4
|
+
require_relative "simplify"
|
|
5
|
+
|
|
6
|
+
module Rubycc
|
|
7
|
+
module IR
|
|
8
|
+
# One function's instruction list, counted once and handed to everyone who
|
|
9
|
+
# needs the count.
|
|
10
|
+
#
|
|
11
|
+
# Three consumers ask the same questions of the same flat list: IR::Simplify
|
|
12
|
+
# decides its rewrites from how often each virtual register is read and
|
|
13
|
+
# written, IR::Promotion decides which registers are worth a machine
|
|
14
|
+
# register from the same counts plus the transient set, and a backend needs
|
|
15
|
+
# that transient set again on every store it emits. Each of them used to
|
|
16
|
+
# walk the list for itself — the census three times over, the transient set
|
|
17
|
+
# twice, the "is every op one we recognize?" test four times — so a function
|
|
18
|
+
# was scanned a dozen times to answer four questions.
|
|
19
|
+
#
|
|
20
|
+
# This object is that answer, computed once on the way through and passed
|
|
21
|
+
# along: IR::Analysis.simplified runs the rewrites and keeps the census they
|
|
22
|
+
# maintained, and Compiler hands the result straight to the backend. Nothing
|
|
23
|
+
# here decides anything — every rule still lives in Simplify and Promotion —
|
|
24
|
+
# and nothing is cached that the list could invalidate, because a rewritten
|
|
25
|
+
# list gets an object of its own.
|
|
26
|
+
class Analysis
|
|
27
|
+
# `function` is the list this census describes (the *rewritten* one, where
|
|
28
|
+
# a rewrite happened), and `known` says whether every op in it is one
|
|
29
|
+
# IR::Simplify can enumerate the reads of. When it is false the counts are
|
|
30
|
+
# not to be trusted and every consumer refuses the function outright,
|
|
31
|
+
# which is the same fail-safe #run applies.
|
|
32
|
+
attr_reader :function, :insts, :param_count, :vreg_count, :reads, :writes
|
|
33
|
+
|
|
34
|
+
# The census of a function nothing has rewritten — what a backend handed a
|
|
35
|
+
# function directly (a test, or any caller that skips the pass) has to
|
|
36
|
+
# fall back on.
|
|
37
|
+
def self.of(function)
|
|
38
|
+
reads, writes = Simplify.census(function.insts, function.vreg_count)
|
|
39
|
+
new(function, reads, writes)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# IR::Simplify.run, plus the census the rewrites kept true as they went.
|
|
43
|
+
def self.simplified(function)
|
|
44
|
+
rewritten, reads, writes = Simplify.run_counted(function)
|
|
45
|
+
new(rewritten, reads, writes)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def initialize(function, reads, writes)
|
|
49
|
+
@function = function
|
|
50
|
+
@insts = function.insts
|
|
51
|
+
@param_count = function.param_count
|
|
52
|
+
@vreg_count = function.vreg_count
|
|
53
|
+
@reads = reads
|
|
54
|
+
@writes = writes
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Whether every op in the list is one whose reads IR::Simplify can
|
|
58
|
+
# enumerate. A function containing anything else is refused whole, by the
|
|
59
|
+
# pass and by every consumer of this object.
|
|
60
|
+
def known?
|
|
61
|
+
!@reads.nil?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# The transient virtual registers (IR::Simplify#transient_flags), as an
|
|
65
|
+
# array indexed by register number. Computed on first ask and kept, which
|
|
66
|
+
# is what stops IR::Promotion and the backend from computing it twice for
|
|
67
|
+
# the same list.
|
|
68
|
+
def transient
|
|
69
|
+
@transient ||=
|
|
70
|
+
if known?
|
|
71
|
+
Simplify.transient_flags(@insts, @param_count, @reads, @writes, @vreg_count)
|
|
72
|
+
else
|
|
73
|
+
EMPTY_FLAGS
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# The answer for a function with no transient at all. Frozen and shared:
|
|
78
|
+
# every read of it is an index that finds nothing.
|
|
79
|
+
EMPTY_FLAGS = [].freeze
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -7,8 +7,8 @@ module Rubycc
|
|
|
7
7
|
# One piece of a by-value aggregate as its convention moves it: the byte
|
|
8
8
|
# `offset` within the aggregate the piece is read from (and written back to
|
|
9
9
|
# at the far end), the `size` of that access, and the `kind` of place it
|
|
10
|
-
# travels in (:gp an integer register, :sse4/:sse8 a vector one, :mem
|
|
11
|
-
# stack eightbyte).
|
|
10
|
+
# travels in (:gp an integer register, :sse4/:sse8/:sse16 a vector one, :mem
|
|
11
|
+
# a stack eightbyte).
|
|
12
12
|
#
|
|
13
13
|
# An aggregate is never moved as a whole — the generator takes it apart into
|
|
14
14
|
# these pieces, loads each into a virtual register and hands the backend one
|
|
@@ -22,6 +22,14 @@ module Rubycc
|
|
|
22
22
|
# single-precision registers on aarch64.
|
|
23
23
|
AbiPiece = Data.define(:offset, :size, :kind)
|
|
24
24
|
|
|
25
|
+
# The piece kinds that consume a vector register, so that a placer counting
|
|
26
|
+
# a request's demand on the vector file names them in one place. :sse4 and
|
|
27
|
+
# :sse8 carry a single and a double; :sse16 carries a whole 16-byte value in
|
|
28
|
+
# one register (AAPCS64's quad-precision `long double`) and, unlike the
|
|
29
|
+
# other two, names the *address* of that value rather than the value itself,
|
|
30
|
+
# since no 8-byte virtual-register slot could hold it.
|
|
31
|
+
FP_KINDS = %i[sse4 sse8 sse16].freeze
|
|
32
|
+
|
|
25
33
|
# How a convention passes one aggregate by value:
|
|
26
34
|
# :registers — `pieces` names each register-borne piece;
|
|
27
35
|
# :memory — the value is laid into the caller's stack argument area
|
|
@@ -120,6 +128,28 @@ module Rubycc
|
|
|
120
128
|
raise NotImplementedError
|
|
121
129
|
end
|
|
122
130
|
|
|
131
|
+
# The target's `long double` format, which decides the 16-byte image the
|
|
132
|
+
# generator builds for a variadic `long double` argument:
|
|
133
|
+
# :x87_extended80 (an explicit-integer-bit 64-bit significand plus a
|
|
134
|
+
# sign/15-bit-exponent halfword, occupying the low ten of sixteen bytes)
|
|
135
|
+
# or :binary128 (IEEE 754 quadruple precision). Both are supersets of
|
|
136
|
+
# binary64, so the conversion from the double rubycc actually holds is
|
|
137
|
+
# exact; see IR::Generator#emit_long_double_image.
|
|
138
|
+
def long_double_format
|
|
139
|
+
raise NotImplementedError
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# How that 16-byte image travels in a variadic call's variable part, as an
|
|
143
|
+
# AggregatePlan over the image (not over Type::LongDouble, whose #size is
|
|
144
|
+
# the 8 bytes rubycc computes in). The two targets disagree completely:
|
|
145
|
+
# System V gives `long double` the X87/X87UP classes, which no register
|
|
146
|
+
# can carry, so the image goes in the stack argument area; AAPCS64 makes
|
|
147
|
+
# it an ordinary quad-precision value that rides one whole vector
|
|
148
|
+
# register. Both align its stack slot to 16 bytes, hence align16.
|
|
149
|
+
def long_double_plan
|
|
150
|
+
raise NotImplementedError
|
|
151
|
+
end
|
|
152
|
+
|
|
123
153
|
# A fresh running placement of one argument list (see the Placer classes).
|
|
124
154
|
def placer
|
|
125
155
|
raise NotImplementedError
|
|
@@ -174,6 +204,20 @@ module Rubycc
|
|
|
174
204
|
Placer.new(self)
|
|
175
205
|
end
|
|
176
206
|
|
|
207
|
+
# psABI 3.2.3: `long double` is the 80-bit x87 extended format, classified
|
|
208
|
+
# X87 (its low eightbyte) and X87UP (its high one). Neither class has a
|
|
209
|
+
# register to be handed out in an argument list, so the value always
|
|
210
|
+
# passes in memory, in a 16-byte slot the psABI aligns to 16 (measured:
|
|
211
|
+
# gcc 13 reserves a pad eightbyte ahead of it when the stack argument
|
|
212
|
+
# area has reached an odd offset).
|
|
213
|
+
def long_double_format
|
|
214
|
+
:x87_extended80
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def long_double_plan
|
|
218
|
+
AggregatePlan.new(mode: :memory, pieces: CallConvention.memory_pieces(16), align16: true)
|
|
219
|
+
end
|
|
220
|
+
|
|
177
221
|
private
|
|
178
222
|
|
|
179
223
|
# Whether any scalar field of `type`, placed at absolute byte offset
|
|
@@ -285,7 +329,7 @@ module Rubycc
|
|
|
285
329
|
def place(request)
|
|
286
330
|
@pad_stack = 0
|
|
287
331
|
need_gp = request.kinds.count(:gp)
|
|
288
|
-
need_sse = request.kinds.count { |kind| kind
|
|
332
|
+
need_sse = request.kinds.count { |kind| FP_KINDS.include?(kind) }
|
|
289
333
|
spills = request.kinds.all?(:mem) ||
|
|
290
334
|
!(@next_gp + need_gp <= @convention.gp_registers &&
|
|
291
335
|
@next_sse + need_sse <= @convention.fp_registers)
|
|
@@ -356,6 +400,24 @@ module Rubycc
|
|
|
356
400
|
Placer.new(self)
|
|
357
401
|
end
|
|
358
402
|
|
|
403
|
+
# AAPCS64 6.4.2: `long double` is IEEE 754 binary128, a Quad-precision
|
|
404
|
+
# Floating-point type, and stage C.1 gives one to the next free SIMD&FP
|
|
405
|
+
# register whole — v[NSRN], read as a 16-byte q register. The rule is the
|
|
406
|
+
# same in the variable part of a variadic call on this ABI (measured:
|
|
407
|
+
# gcc 13 puts the ninth argument of "printf(fmt, 1.0..7.0, ld, 9)" in q7),
|
|
408
|
+
# unlike the Apple variant, which stacks every anonymous argument.
|
|
409
|
+
# An overflowing quad spills to an NSAA the standard rounds up to the
|
|
410
|
+
# type's 16-byte natural alignment (stage C.13), hence align16.
|
|
411
|
+
def long_double_format
|
|
412
|
+
:binary128
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
def long_double_plan
|
|
416
|
+
AggregatePlan.new(mode: :registers,
|
|
417
|
+
pieces: [AbiPiece.new(offset: 0, size: 16, kind: :sse16)],
|
|
418
|
+
align16: true)
|
|
419
|
+
end
|
|
420
|
+
|
|
359
421
|
private
|
|
360
422
|
|
|
361
423
|
# Whether `type` is built entirely out of one floating type, and of how
|
|
@@ -438,8 +500,8 @@ module Rubycc
|
|
|
438
500
|
def place(request)
|
|
439
501
|
@pad_gp = 0
|
|
440
502
|
@pad_stack = 0
|
|
441
|
-
need_fp = request.kinds.count { |kind| kind
|
|
442
|
-
return place_fp(need_fp, request.mem_eightbytes) if need_fp.positive?
|
|
503
|
+
need_fp = request.kinds.count { |kind| FP_KINDS.include?(kind) }
|
|
504
|
+
return place_fp(need_fp, request.align16, request.mem_eightbytes) if need_fp.positive?
|
|
443
505
|
|
|
444
506
|
need_gp = request.kinds.count(:gp)
|
|
445
507
|
return place_gp(need_gp, request.align16, request.mem_eightbytes) if need_gp.positive?
|
|
@@ -451,13 +513,18 @@ module Rubycc
|
|
|
451
513
|
|
|
452
514
|
private
|
|
453
515
|
|
|
454
|
-
|
|
516
|
+
# A spilled vector argument aligns NSAA to its own natural alignment the
|
|
517
|
+
# way an integer-register aggregate does; only a quad ever asks for 16
|
|
518
|
+
# (align16), since a single or a double is 8-aligned and a stack slot
|
|
519
|
+
# already is.
|
|
520
|
+
def place_fp(count, align16, mem_eightbytes)
|
|
455
521
|
if @nsrn + count <= @convention.fp_registers
|
|
456
522
|
@nsrn += count
|
|
457
523
|
:registers
|
|
458
524
|
else
|
|
459
525
|
@nsrn = @convention.fp_registers
|
|
460
|
-
@
|
|
526
|
+
@pad_stack = 1 if align16 && @nsaa.odd?
|
|
527
|
+
@nsaa += @pad_stack + mem_eightbytes
|
|
461
528
|
:stack
|
|
462
529
|
end
|
|
463
530
|
end
|