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
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "../compile_error"
|
|
4
4
|
require_relative "../ir/ir"
|
|
5
|
+
require_relative "../ir/analysis"
|
|
6
|
+
require_relative "../ir/promotion"
|
|
7
|
+
require_relative "../ir/simplify"
|
|
8
|
+
require_relative "slot_residency"
|
|
5
9
|
|
|
6
10
|
module Rubycc
|
|
7
11
|
module Backend
|
|
@@ -20,11 +24,12 @@ module Rubycc
|
|
|
20
24
|
|
|
21
25
|
# AArch64 (ARM64) code generator, the second backend behind the same
|
|
22
26
|
# IR::Function -> Result contract the x86_64 one honors. It keeps the
|
|
23
|
-
# spill-everything strategy
|
|
24
|
-
# slot and each IR instruction loads
|
|
25
|
-
# computes, and stores the result back
|
|
26
|
-
# entirely different: fixed-length 32-bit
|
|
27
|
-
# file, and load/store addressing that shapes
|
|
27
|
+
# spill-everything strategy for every value but the promoted one — each
|
|
28
|
+
# virtual register owns an 8-byte stack slot and each IR instruction loads
|
|
29
|
+
# its operands into scratch registers, computes, and stores the result back
|
|
30
|
+
# — but the machine underneath is entirely different: fixed-length 32-bit
|
|
31
|
+
# instructions, a flat register file, and load/store addressing that shapes
|
|
32
|
+
# the frame layout.
|
|
28
33
|
#
|
|
29
34
|
# Frame layout (frame-base-relative, positive offsets). Unlike x86_64's
|
|
30
35
|
# rbp-negative displacements, every slot is addressed as [base + off] with a
|
|
@@ -32,11 +37,11 @@ module Rubycc
|
|
|
32
37
|
# 12-bit immediate by the access size (reaching 0..32760 for a 64-bit load)
|
|
33
38
|
# while the signed form is only a 9-bit unscaled window (-256..255) that a
|
|
34
39
|
# modest frame overruns at once. From the base upward the frame holds the
|
|
35
|
-
# outgoing argument area, the saved frame record (x29/x30), the vreg slots
|
|
36
|
-
#
|
|
37
|
-
# immediate is reached by composing its
|
|
38
|
-
# add-immediate(s) — the path is built
|
|
39
|
-
# on for large frames.
|
|
40
|
+
# outgoing argument area, the saved frame record (x29/x30), the vreg slots,
|
|
41
|
+
# the stack objects and the promoted registers' save slots. A slot whose
|
|
42
|
+
# offset still overflows the scaled immediate is reached by composing its
|
|
43
|
+
# address into a scratch register with add-immediate(s) — the path is built
|
|
44
|
+
# in from the start rather than bolted on for large frames.
|
|
40
45
|
#
|
|
41
46
|
# The frame base is sp itself in an ordinary function, which costs nothing
|
|
42
47
|
# and leaves x29 free. It is x29 in a function containing :alloca, because
|
|
@@ -78,7 +83,8 @@ module Rubycc
|
|
|
78
83
|
# floating arguments arrive in v0..v7, allocated from a counter of their
|
|
79
84
|
# own, and a floating result comes back in v0. The two sequences are
|
|
80
85
|
# independent, exactly as System V's integer and xmm sequences are, which is
|
|
81
|
-
# why the IR's :gp/:sse4/:sse8 tags carry over unchanged
|
|
86
|
+
# why the IR's :gp/:sse4/:sse8 tags carry over unchanged (:sse16, a whole
|
|
87
|
+
# 16-byte value in one vector register, is this target's alone). The generator
|
|
82
88
|
# classifies against this target's register budget (IR::CallConvention),
|
|
83
89
|
# so a :gp tag really does mean one of the eight and a :mem tag really does
|
|
84
90
|
# mean the stack — no seventh integer argument is spilled here that AAPCS64
|
|
@@ -130,6 +136,13 @@ module Rubycc
|
|
|
130
136
|
# frame addressable (see #emit_alloca). Every IR op the generator can hand
|
|
131
137
|
# this backend is now lowered; there is nothing left it refuses.
|
|
132
138
|
class AArch64
|
|
139
|
+
# Every slot access goes through #load_reg / #store_reg, which use this to
|
|
140
|
+
# skip a reload of a value the previous instruction has just left in a
|
|
141
|
+
# register. See slot_residency.rb for why "nothing has been emitted since"
|
|
142
|
+
# is a sufficient safety condition, and #emit_instruction's :label case
|
|
143
|
+
# for the one state change it cannot see.
|
|
144
|
+
include SlotResidency
|
|
145
|
+
|
|
133
146
|
# Result of compiling one function, the same shape the x86_64 backend
|
|
134
147
|
# returns: `bytes` the machine code, `symbols` an array of
|
|
135
148
|
# { name:, offset:, size: }, and `relocations` an array of kind-tagged
|
|
@@ -201,6 +214,29 @@ module Rubycc
|
|
|
201
214
|
FA = 16
|
|
202
215
|
FB = 17
|
|
203
216
|
|
|
217
|
+
# The registers whole-function promotion hands out, in the order it hands
|
|
218
|
+
# them out (see IR::Promotion and #promotion_assignment). All ten are
|
|
219
|
+
# callee-saved under AAPCS64 (5.1.1: x19..x28 are preserved across a
|
|
220
|
+
# call), which is what lets a promoted value stay put across one, and all
|
|
221
|
+
# ten are clear of everything else this backend names: the scratch
|
|
222
|
+
# registers x9..x15, the argument/result registers x0..x8, the frame
|
|
223
|
+
# pointer x29 and the link register x30.
|
|
224
|
+
#
|
|
225
|
+
# The three callee-saved-looking registers left out are left out on
|
|
226
|
+
# purpose. x18 is the platform register, reserved by AAPCS64 5.1.1 for
|
|
227
|
+
# whatever the running platform ABI wants it for, so no compiler may use
|
|
228
|
+
# it as a general one. x16 and x17 (IP0/IP1) are corruptible by any
|
|
229
|
+
# branch: the linker is free to insert a veneer at a `bl` site, and a
|
|
230
|
+
# veneer clobbers them.
|
|
231
|
+
#
|
|
232
|
+
# Being disjoint from every register the residency table is keyed by is
|
|
233
|
+
# what lets an instruction that writes a promoted register leave that
|
|
234
|
+
# table standing (see SlotResidency#note_register_clobbered), and #load_reg
|
|
235
|
+
# keeps the disjointness true from the other side by never recording a
|
|
236
|
+
# residency against one.
|
|
237
|
+
PROMOTION_REGISTERS = (19..28).to_a.freeze
|
|
238
|
+
PROMOTION_REGISTER_SET = PROMOTION_REGISTERS.to_set.freeze
|
|
239
|
+
|
|
204
240
|
# Special register numbers. In the load/store, add/sub-immediate and
|
|
205
241
|
# stp/ldp encodings a register field of 31 denotes the stack pointer; in
|
|
206
242
|
# the data-processing (arithmetic/logical shifted-register) encodings the
|
|
@@ -255,8 +291,28 @@ module Rubycc
|
|
|
255
291
|
flt: 4, fle: 9, fgt: 12, fge: 10
|
|
256
292
|
}.freeze
|
|
257
293
|
|
|
258
|
-
|
|
294
|
+
# `analysis` is the census of `ir_func`'s instruction list (IR::Analysis).
|
|
295
|
+
# The compiler passes the one IR::Simplify already took on its way
|
|
296
|
+
# through; a caller that hands over a function directly gets it taken
|
|
297
|
+
# here.
|
|
298
|
+
def compile(ir_func, analysis = IR::Analysis.of(ir_func))
|
|
259
299
|
@code = +"".b
|
|
300
|
+
reset_slot_residency
|
|
301
|
+
# Slots this function never has to write: an expression temporary whose
|
|
302
|
+
# one reader is the instruction right behind its producer stays in the
|
|
303
|
+
# register it was computed in (see IR::Simplify#transient_flags and
|
|
304
|
+
# #store_reg). The answer is a property of the instruction list, so it
|
|
305
|
+
# is taken once here rather than rediscovered per instruction, and it
|
|
306
|
+
# arrives as an array indexed by vreg number — the numbers are dense and
|
|
307
|
+
# small, and this is asked on every store the backend emits.
|
|
308
|
+
@transient = analysis.transient
|
|
309
|
+
# Slots this function does not have at all: a promoted value lives in
|
|
310
|
+
# one callee-saved register from the prologue to the epilogue, so its
|
|
311
|
+
# slot is never read, written or named (see #promotion_assignment,
|
|
312
|
+
# #load_reg and #store_reg). Like @transient this is a property of the
|
|
313
|
+
# instruction list, decided once here, and indexed by vreg number for
|
|
314
|
+
# the same reason.
|
|
315
|
+
@promoted = promotion_assignment(ir_func, analysis)
|
|
260
316
|
# @labels maps a label id to its resolved byte offset; @fixups collects
|
|
261
317
|
# [patch_offset, label_id, kind] for each forward/backward branch whose
|
|
262
318
|
# immediate is written once every label offset is known.
|
|
@@ -282,13 +338,32 @@ module Rubycc
|
|
|
282
338
|
|
|
283
339
|
private
|
|
284
340
|
|
|
341
|
+
# Binds the best promotion candidates to PROMOTION_REGISTERS in order,
|
|
342
|
+
# returning the vreg -> register table the whole backend consults (an
|
|
343
|
+
# array; a vreg that owns no register indexes nil). Taking the first few
|
|
344
|
+
# of an ordered list is the entire allocation: one register each, held for
|
|
345
|
+
# the function's length, so there is no interference to resolve and
|
|
346
|
+
# nothing to undo when the registers run out — the candidates past the
|
|
347
|
+
# tenth simply keep their slots.
|
|
348
|
+
#
|
|
349
|
+
# @promoted_registers is the same binding in assignment order, which is
|
|
350
|
+
# the order the save slots are laid out in (#layout_frame).
|
|
351
|
+
def promotion_assignment(ir_func, analysis)
|
|
352
|
+
vregs = IR::Promotion.candidates(ir_func, analysis).first(PROMOTION_REGISTERS.size)
|
|
353
|
+
@promoted_registers = PROMOTION_REGISTERS.first(vregs.size)
|
|
354
|
+
promoted = Array.new(ir_func.vreg_count)
|
|
355
|
+
vregs.each_with_index { |vreg, index| promoted[vreg] = PROMOTION_REGISTERS[index] }
|
|
356
|
+
promoted
|
|
357
|
+
end
|
|
358
|
+
|
|
285
359
|
# Computes the frame's size and every object's base offset. From sp
|
|
286
360
|
# upward: the outgoing argument area (empty unless some call in this
|
|
287
361
|
# function passes an argument on the stack), the 16-byte saved record,
|
|
288
362
|
# the vreg slots (8 bytes each, the region rounded to 16 so the objects
|
|
289
363
|
# stay 16-aligned), then each stack object at a 16-byte-aligned size above
|
|
290
|
-
# the previous
|
|
291
|
-
#
|
|
364
|
+
# the previous, then one 8-byte save slot per promoted register. All
|
|
365
|
+
# offsets are non-negative displacements from the frame base, which this
|
|
366
|
+
# method also decides (see #frame_base_register).
|
|
292
367
|
def layout_frame(vreg_count, stack_objects, insts, variadic)
|
|
293
368
|
# Whether this function allocates dynamically decides two things at
|
|
294
369
|
# once: which register names the fixed frame (see #frame_base_register)
|
|
@@ -309,6 +384,28 @@ module Rubycc
|
|
|
309
384
|
@object_offsets << running
|
|
310
385
|
running += align16(object_size)
|
|
311
386
|
end
|
|
387
|
+
# One save slot per promoted register, above every object. The
|
|
388
|
+
# registers are callee-saved, so a function that takes one over has to
|
|
389
|
+
# give it back exactly as it found it. This end of the frame is the one
|
|
390
|
+
# place a new region can go without moving anything: the outgoing
|
|
391
|
+
# argument area has to stay at the bottom (AAPCS64 has the callee read
|
|
392
|
+
# its stack arguments from the sp of the `bl`), the saved record and the
|
|
393
|
+
# vreg slots are what every offset in the body is measured against, and
|
|
394
|
+
# the objects sit between. Putting the saves last therefore leaves every
|
|
395
|
+
# other offset exactly what it would have been without promotion. Their
|
|
396
|
+
# number is odd as often as not, but nothing above them cares: the one
|
|
397
|
+
# region that has to end exactly at the frame's top is the variadic
|
|
398
|
+
# register-save area, and a variadic function has no promoted register
|
|
399
|
+
# at all (IR::Promotion refuses one), so the two never meet. The single
|
|
400
|
+
# align16 below is what fixes the frame size, so sp stays 16-aligned
|
|
401
|
+
# whatever the count. They are laid out in assignment order,
|
|
402
|
+
# a function of the instruction list, so the frame stays deterministic
|
|
403
|
+
# (N4).
|
|
404
|
+
@promoted_saves = @promoted_registers.map do |reg|
|
|
405
|
+
offset = running
|
|
406
|
+
running += 8
|
|
407
|
+
[reg, offset]
|
|
408
|
+
end
|
|
312
409
|
# A variadic function reserves the argument register-save area at the
|
|
313
410
|
# very top of the frame — just below the caller's sp, exactly where the
|
|
314
411
|
# AArch64 C library expects __gr_top/__vr_top to point. The vector area
|
|
@@ -333,12 +430,18 @@ module Rubycc
|
|
|
333
430
|
# so the saved record above it — and sp itself at every call — stays
|
|
334
431
|
# 16-aligned. Sizing it for the widest call lets every call share the one
|
|
335
432
|
# area, since only one call is in flight at a time.
|
|
433
|
+
#
|
|
434
|
+
# A :pad_stack slot occupies an eightbyte of the area exactly as a :mem
|
|
435
|
+
# one does — it is the gap that 16-aligns the argument behind it — so it
|
|
436
|
+
# is counted here on the same footing. #place_arguments counts the two
|
|
437
|
+
# together as well, and the two counts have to agree or a call would write
|
|
438
|
+
# past the area the frame reserved for it.
|
|
336
439
|
def outgoing_argument_bytes(insts)
|
|
337
440
|
widest = 0
|
|
338
441
|
insts.each do |inst|
|
|
339
442
|
next unless inst.op == :call || inst.op == :call_indirect
|
|
340
443
|
|
|
341
|
-
count = inst.b.count { |_vreg, kind| kind == :mem }
|
|
444
|
+
count = inst.b.count { |_vreg, kind| kind == :mem || kind == :pad_stack }
|
|
342
445
|
widest = count if count > widest
|
|
343
446
|
end
|
|
344
447
|
align16(widest * 8)
|
|
@@ -377,10 +480,32 @@ module Rubycc
|
|
|
377
480
|
adjust_sp(@frame_size, sub: true)
|
|
378
481
|
emit_save_record(store: true, base: SP)
|
|
379
482
|
emit_add_imm(FP, SP, 0, shift12: false) if @uses_alloca # mov x29, sp
|
|
483
|
+
# Before anything writes a promoted register — which the parameter
|
|
484
|
+
# spilling right below is the first thing to do, a promoted parameter
|
|
485
|
+
# being moved straight from the register it arrived in — and after the
|
|
486
|
+
# frame base is established, since the saves are addressed against it.
|
|
487
|
+
emit_save_promoted_registers
|
|
380
488
|
spill_parameters(param_kinds)
|
|
381
489
|
save_argument_registers if variadic
|
|
382
490
|
end
|
|
383
491
|
|
|
492
|
+
# Saves the promoted registers into their frame slots, one "str Xn,
|
|
493
|
+
# [base + off]" apiece. A store into the frame rather than a push (or an
|
|
494
|
+
# stp pair) keeps sp's 16-byte alignment a property of the single frame
|
|
495
|
+
# adjustment in the prologue instead of the parity of the register count.
|
|
496
|
+
def emit_save_promoted_registers
|
|
497
|
+
@promoted_saves.each { |reg, offset| store_frame_at(reg, offset) }
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
# Puts the caller's values back, the exact inverse of
|
|
501
|
+
# #emit_save_promoted_registers. #emit_epilogue carries it, and an
|
|
502
|
+
# epilogue is emitted at every :ret rather than once per function, so a
|
|
503
|
+
# function with several returns restores on each of them and no path
|
|
504
|
+
# leaves with a callee-saved register holding this function's value.
|
|
505
|
+
def emit_restore_promoted_registers
|
|
506
|
+
@promoted_saves.each { |reg, offset| load_frame_at(reg, offset) }
|
|
507
|
+
end
|
|
508
|
+
|
|
384
509
|
# Spills every argument register into the variadic save area so a later
|
|
385
510
|
# :va_start can hand __builtin_va_arg a pointer to each. The eight integer
|
|
386
511
|
# registers go into the 8-byte slots at the top of the frame and the eight
|
|
@@ -447,8 +572,16 @@ module Rubycc
|
|
|
447
572
|
# the end of the scope that allocated it. The record is then reloaded and
|
|
448
573
|
# the fixed frame released exactly as in an ordinary function (sp and x29
|
|
449
574
|
# are equal by then, so the reload needs no special base).
|
|
575
|
+
#
|
|
576
|
+
# The promoted registers are given back here, after the return value has
|
|
577
|
+
# been loaded (it may itself come out of one) and — in an alloca function
|
|
578
|
+
# — after sp has been brought back to the fixed frame, which is the
|
|
579
|
+
# moment the save slots become addressable again through sp. They are in
|
|
580
|
+
# fact reached through x29 there, so the order is not what makes this
|
|
581
|
+
# correct; it is what keeps the two cases reading the same way.
|
|
450
582
|
def emit_epilogue
|
|
451
583
|
emit_add_imm(SP, FP, 0, shift12: false) if @uses_alloca # mov sp, x29
|
|
584
|
+
emit_restore_promoted_registers
|
|
452
585
|
emit_save_record(store: false)
|
|
453
586
|
adjust_sp(@frame_size, sub: false)
|
|
454
587
|
emit_word(0xD65F03C0) # ret (branch to x30)
|
|
@@ -470,12 +603,14 @@ module Rubycc
|
|
|
470
603
|
end
|
|
471
604
|
end
|
|
472
605
|
|
|
473
|
-
# Brings each incoming argument into its
|
|
474
|
-
#
|
|
475
|
-
#
|
|
476
|
-
#
|
|
477
|
-
#
|
|
478
|
-
#
|
|
606
|
+
# Brings each incoming argument into its home — its parameter slot, or
|
|
607
|
+
# the callee-saved register it was promoted into, which #store_reg
|
|
608
|
+
# decides — so it reads back like any other vreg. Integer/pointer
|
|
609
|
+
# parameters come out of x0..x7 and floating ones out of v0..v7, each
|
|
610
|
+
# sequence advancing its own counter; a :mem parameter is copied down
|
|
611
|
+
# from the caller's stack argument area (through the A scratch, which is
|
|
612
|
+
# not an argument register, so the copies never disturb an argument still
|
|
613
|
+
# to be spilled, or through its promoted register) as a whole eightbyte,
|
|
479
614
|
# which is right for a narrow or floating value too since the slot
|
|
480
615
|
# discipline only promises its low bytes. A kind that would overrun its
|
|
481
616
|
# register file is a generator contract violation and raises.
|
|
@@ -491,8 +626,14 @@ module Rubycc
|
|
|
491
626
|
store_reg(ARG_REGISTERS[next_gp], i)
|
|
492
627
|
next_gp += 1
|
|
493
628
|
when :mem
|
|
494
|
-
|
|
495
|
-
|
|
629
|
+
# Straight into the register a promoted parameter lives in: the
|
|
630
|
+
# copy down from the caller's area is a load like any other, and
|
|
631
|
+
# its destination is a free field, so nothing is gained by routing
|
|
632
|
+
# it through A first. #store_reg then emits nothing at all, the
|
|
633
|
+
# value being already home.
|
|
634
|
+
target = @promoted[i] || A
|
|
635
|
+
load_frame_at(target, incoming_stack_offset(next_stack))
|
|
636
|
+
store_reg(target, i)
|
|
496
637
|
next_stack += 1
|
|
497
638
|
when :pad
|
|
498
639
|
# An even-pair alignment pad consumes one integer register but binds
|
|
@@ -520,14 +661,23 @@ module Rubycc
|
|
|
520
661
|
end
|
|
521
662
|
|
|
522
663
|
def emit_instruction(inst)
|
|
664
|
+
# Once, at the top, rather than in each lowering that consults the
|
|
665
|
+
# table. An instruction whose operands are all promoted loads nothing,
|
|
666
|
+
# so it can reach #store_result — which revalidates the table — without
|
|
667
|
+
# any of the loads that would otherwise have discarded a stale one
|
|
668
|
+
# first. Refreshing here means the table is either true or empty by the
|
|
669
|
+
# time any lowering touches it, whatever the instruction turns out to
|
|
670
|
+
# be.
|
|
671
|
+
refresh_slot_residency
|
|
523
672
|
case inst.op
|
|
524
673
|
when :const then emit_const(inst.dst, inst.a, inst.size)
|
|
525
674
|
when :copy then emit_copy(inst.dst, inst.a)
|
|
526
|
-
when :add then emit_arith(inst, ADD_SHIFTED)
|
|
675
|
+
when :add then emit_arith(inst, ADD_SHIFTED, commutative: true)
|
|
676
|
+
when :scaled_add then emit_scaled_add(inst.dst, inst.a, inst.b, inst.size)
|
|
527
677
|
when :sub then emit_arith(inst, SUB_SHIFTED)
|
|
528
|
-
when :and then emit_arith(inst, AND_SHIFTED)
|
|
529
|
-
when :or then emit_arith(inst, ORR_SHIFTED)
|
|
530
|
-
when :xor then emit_arith(inst, EOR_SHIFTED)
|
|
678
|
+
when :and then emit_arith(inst, AND_SHIFTED, commutative: true)
|
|
679
|
+
when :or then emit_arith(inst, ORR_SHIFTED, commutative: true)
|
|
680
|
+
when :xor then emit_arith(inst, EOR_SHIFTED, commutative: true)
|
|
531
681
|
when :mul then emit_mul(inst.dst, inst.a, inst.b, inst.size)
|
|
532
682
|
when :div then emit_divmod(inst.dst, inst.a, inst.b, inst.size, signed: true, remainder: false)
|
|
533
683
|
when :mod then emit_divmod(inst.dst, inst.a, inst.b, inst.size, signed: true, remainder: true)
|
|
@@ -538,10 +688,17 @@ module Rubycc
|
|
|
538
688
|
when :shr then emit_shift(inst.dst, inst.a, inst.b, inst.size, LSRV)
|
|
539
689
|
when :neg then emit_neg(inst.dst, inst.a, inst.size)
|
|
540
690
|
when :eq, :ne, :lt, :le, :gt, :ge, :ult, :ule, :ugt, :uge
|
|
541
|
-
emit_comparison(inst.dst, inst.a, inst.b, CONDITIONS.fetch(inst.op), inst.size
|
|
691
|
+
emit_comparison(inst.dst, inst.a, inst.b, CONDITIONS.fetch(inst.op), inst.size,
|
|
692
|
+
commutative: inst.op == :eq || inst.op == :ne)
|
|
542
693
|
when :sext then emit_sext(inst.dst, inst.a, inst.size)
|
|
543
694
|
when :zext then emit_zext(inst.dst, inst.a, inst.size)
|
|
544
|
-
when :label
|
|
695
|
+
when :label
|
|
696
|
+
# The one place a register's meaning changes without an instruction
|
|
697
|
+
# being emitted: control may arrive here from a branch whose registers
|
|
698
|
+
# hold something else entirely, so nothing may be assumed resident
|
|
699
|
+
# past a label (see SlotResidency).
|
|
700
|
+
forget_slot_residency
|
|
701
|
+
@labels[inst.a] = @code.bytesize
|
|
545
702
|
when :jump then emit_b(inst.a)
|
|
546
703
|
when :jump_if_zero then emit_jump_if_zero(inst.a, inst.b)
|
|
547
704
|
when :call then emit_call(inst.dst, inst.a, inst.b, inst.size)
|
|
@@ -563,6 +720,7 @@ module Rubycc
|
|
|
563
720
|
when :call_indirect then emit_call_indirect(inst.dst, inst.a, inst.b, inst.size)
|
|
564
721
|
when :mulhi then emit_mulhi(inst.dst, inst.a, inst.b)
|
|
565
722
|
when :bit_scan then emit_bit_scan(inst.dst, inst.a, inst.b, inst.size)
|
|
723
|
+
when :popcount then emit_popcount(inst.dst, inst.a, inst.size)
|
|
566
724
|
when :string_addr then emit_symbol_address(inst.dst, kind: :string, string_id: inst.a)
|
|
567
725
|
when :global_addr then emit_symbol_address(inst.dst, kind: :global, symbol: inst.a)
|
|
568
726
|
when :func_addr then emit_symbol_address(inst.dst, kind: :func, symbol: inst.a)
|
|
@@ -585,34 +743,52 @@ module Rubycc
|
|
|
585
743
|
# 32-bit value whose W-register move zeroes the slot's high half, matching
|
|
586
744
|
# the x86_64 backend's "mov eax, imm32" behavior.
|
|
587
745
|
def emit_const(dst, value, size)
|
|
588
|
-
|
|
589
|
-
|
|
746
|
+
target = result_register(dst)
|
|
747
|
+
materialize(target, value, size == 8 ? 64 : 32)
|
|
748
|
+
store_result(target, dst, only_wrote: target) # movz/movk write target alone
|
|
590
749
|
end
|
|
591
750
|
|
|
592
|
-
# :copy — a 64-bit
|
|
751
|
+
# :copy — a 64-bit transfer between the two values' homes, whichever
|
|
752
|
+
# those are: slot to slot, slot to register, register to slot, or one
|
|
753
|
+
# `mov` between two promoted registers.
|
|
593
754
|
def emit_copy(dst, src)
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
# combined with a shifted-register
|
|
600
|
-
# size (64-bit for size 8,
|
|
601
|
-
# half is zeroed for free).
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
755
|
+
target = result_register(dst)
|
|
756
|
+
load_reg(target, src)
|
|
757
|
+
store_result(target, dst, only_wrote: target) # the load wrote target, or nothing
|
|
758
|
+
end
|
|
759
|
+
|
|
760
|
+
# An arithmetic/logical binary op, combined with a shifted-register
|
|
761
|
+
# instruction whose width follows the IR size (64-bit for size 8,
|
|
762
|
+
# otherwise a 32-bit W-register op whose upper half is zeroed for free).
|
|
763
|
+
# All three of Rd, Rn and Rm are free fields, so each end is named
|
|
764
|
+
# wherever it lives: a promoted destination and two promoted operands
|
|
765
|
+
# make the whole instruction one word with no traffic around it.
|
|
766
|
+
def emit_arith(inst, base_table, commutative: false)
|
|
767
|
+
rn, rm = binary_operand_registers(inst.a, inst.b, commutative: commutative)
|
|
768
|
+
rd = result_register(inst.dst)
|
|
769
|
+
emit_word(base_table[width(inst.size)] | (rm << 16) | (rn << 5) | rd)
|
|
770
|
+
store_result(rd, inst.dst, only_wrote: rd) # one word, Rd its only written field
|
|
771
|
+
end
|
|
772
|
+
|
|
773
|
+
# :scaled_add — dst <- base + index * element_size, the address a
|
|
774
|
+
# subscript forms. The shifted-register add carries the scale in its imm6
|
|
775
|
+
# field as a left shift of the second operand, so the element-size
|
|
776
|
+
# multiply and the add to the base are one instruction. It is always the
|
|
777
|
+
# 64-bit (X) form: the value being computed is a pointer.
|
|
778
|
+
def emit_scaled_add(dst, base_vreg, index_vreg, element_size)
|
|
779
|
+
rn, rm = binary_operand_registers(base_vreg, index_vreg)
|
|
780
|
+
rd = result_register(dst)
|
|
781
|
+
shift = SHIFTED_SCALES.fetch(element_size)
|
|
782
|
+
emit_word(ADD_SHIFTED[64] | (rm << 16) | (shift << 10) | (rn << 5) | rd)
|
|
783
|
+
store_result(rd, dst, only_wrote: rd) # one word, Rd its only written field
|
|
607
784
|
end
|
|
608
785
|
|
|
609
786
|
# :mul — Rd = Rn * Rm, encoded as `madd Rd, Rn, Rm, xzr`.
|
|
610
787
|
def emit_mul(dst, a, b, size)
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
store_reg(A, dst)
|
|
788
|
+
rn, rm = binary_operand_registers(a, b, commutative: true)
|
|
789
|
+
rd = result_register(dst)
|
|
790
|
+
emit_word(MADD_ZERO[width(size)] | (rm << 16) | (rn << 5) | rd)
|
|
791
|
+
store_result(rd, dst, only_wrote: rd) # one word, Rd its only written field
|
|
616
792
|
end
|
|
617
793
|
|
|
618
794
|
# :mulhi — the unsigned high 64 bits of a 64x64 product, encoded as
|
|
@@ -623,10 +799,10 @@ module Rubycc
|
|
|
623
799
|
# of-a-32x32-product need for it here), so this always runs on the X
|
|
624
800
|
# registers regardless of the IR size.
|
|
625
801
|
def emit_mulhi(dst, a, b)
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
emit_word(UMULH | (
|
|
629
|
-
|
|
802
|
+
rn, rm = binary_operand_registers(a, b, commutative: true)
|
|
803
|
+
rd = result_register(dst)
|
|
804
|
+
emit_word(UMULH | (rm << 16) | (rn << 5) | rd)
|
|
805
|
+
store_result(rd, dst, only_wrote: rd) # one word, Rd its only written field
|
|
630
806
|
end
|
|
631
807
|
|
|
632
808
|
# :bit_scan — count the zero bits of a's value (__builtin_ctz/clz and
|
|
@@ -652,12 +828,69 @@ module Rubycc
|
|
|
652
828
|
# promises nothing there and the x86-64 backend cannot match it anyway.
|
|
653
829
|
def emit_bit_scan(dst, src_vreg, direction, size)
|
|
654
830
|
w = width(size)
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
831
|
+
rn = operand_register(src_vreg, A)
|
|
832
|
+
rd = result_register(dst)
|
|
833
|
+
if direction == :forward
|
|
834
|
+
emit_word(RBIT[w] | (rn << 5) | rd)
|
|
835
|
+
rn = rd # the reversal is what CLZ counts, wherever it landed
|
|
836
|
+
end
|
|
837
|
+
emit_word(CLZ[w] | (rn << 5) | rd)
|
|
838
|
+
store_result(rd, dst, only_wrote: rd) # RBIT and CLZ both write rd and nothing else
|
|
839
|
+
end
|
|
840
|
+
|
|
841
|
+
# :popcount — how many bits of a's value are set (__builtin_popcount and
|
|
842
|
+
# its "l"/"ll" forms). AArch64's population count, CNT, is an AdvSIMD
|
|
843
|
+
# instruction: it counts within each byte of a *vector* register, so using
|
|
844
|
+
# it would mean moving the operand across register files and then summing
|
|
845
|
+
# the byte counts anyway (ADDV). The general-register expansion below
|
|
846
|
+
# avoids the crossing entirely, and is the same divide-and-conquer sum
|
|
847
|
+
# (SWAR) the x86-64 backend emits — where the derivation of the four
|
|
848
|
+
# stages and of their masks is written out (backend/x86_64.rb
|
|
849
|
+
# #emit_popcount), that being the one place worth stating it.
|
|
850
|
+
#
|
|
851
|
+
# Only the final stage differs in what it can name: the multiplier and the
|
|
852
|
+
# masks are all bitmask immediates here (repeating bit patterns, which is
|
|
853
|
+
# exactly what "logical (immediate)" encodes), so no constant has to be
|
|
854
|
+
# built with movz/movk — the multiply's operand does need a register, but
|
|
855
|
+
# one `mov` of an immediate fills it.
|
|
856
|
+
#
|
|
857
|
+
# A runs the value through the stages and B holds the partner each stage
|
|
858
|
+
# shifts out of it. A is loaded rather than read where it lies because
|
|
859
|
+
# every stage overwrites it, so a promoted operand would be destroyed.
|
|
860
|
+
def emit_popcount(dst, src_vreg, size)
|
|
861
|
+
w = width(size)
|
|
862
|
+
load_reg(A, src_vreg) # A = value to count
|
|
863
|
+
emit_popcount_lsr(B, A, 1, w) # B = x >> 1
|
|
864
|
+
emit_popcount_and(B, B, POPCOUNT_PAIR_IMMS, w) # B &= 0x5555...
|
|
865
|
+
emit_word(SUB_SHIFTED[w] | (B << 16) | (A << 5) | A) # A = pair counts
|
|
866
|
+
emit_popcount_and(B, A, POPCOUNT_NIBBLE_IMMS, w) # B = x & 0x3333...
|
|
867
|
+
emit_popcount_lsr(A, A, 2, w) # A = x >> 2
|
|
868
|
+
emit_popcount_and(A, A, POPCOUNT_NIBBLE_IMMS, w) # A &= 0x3333...
|
|
869
|
+
emit_word(ADD_SHIFTED[w] | (B << 16) | (A << 5) | A) # A = nibble counts
|
|
870
|
+
emit_popcount_lsr(B, A, 4, w) # B = x >> 4
|
|
871
|
+
emit_word(ADD_SHIFTED[w] | (B << 16) | (A << 5) | A) # A = x + (x >> 4)
|
|
872
|
+
emit_popcount_and(A, A, POPCOUNT_BYTE_IMMS, w) # A = one count per byte
|
|
873
|
+
emit_word(MOV_IMM[w] | (POPCOUNT_ONES_IMMS << 10) | (XZR << 5) | B) # B = 0x0101...
|
|
874
|
+
emit_word(MADD_ZERO[w] | (B << 16) | (A << 5) | A) # A = the bytes, summed
|
|
875
|
+
emit_popcount_lsr(A, A, size * 8 - 8, w) # A = the top byte
|
|
658
876
|
store_reg(A, dst)
|
|
659
877
|
end
|
|
660
878
|
|
|
879
|
+
# `lsr Rd, Rn, #shift` at the count's width: the shift-by-immediate the
|
|
880
|
+
# architecture has no opcode of its own for, being the UBFM that keeps the
|
|
881
|
+
# field from `shift` up to the register's top bit and right-aligns it.
|
|
882
|
+
def emit_popcount_lsr(rd, rn, shift, w)
|
|
883
|
+
emit_word(LSR_IMM[w] | (shift << 16) | ((w - 1) << 10) | (rn << 5) | rd)
|
|
884
|
+
end
|
|
885
|
+
|
|
886
|
+
# `and Rd, Rn, #mask` at the count's width, the mask named by the imms
|
|
887
|
+
# field that encodes its repeating pattern (see POPCOUNT_PAIR_IMMS). Every
|
|
888
|
+
# mask this expansion needs starts its run of ones at bit 0, so immr — the
|
|
889
|
+
# rotation — is zero throughout and is not a parameter.
|
|
890
|
+
def emit_popcount_and(rd, rn, imms, w)
|
|
891
|
+
emit_word(AND_IMM[w] | (imms << 10) | (rn << 5) | rd)
|
|
892
|
+
end
|
|
893
|
+
|
|
661
894
|
# :alloca — dynamic stack allocation (__builtin_alloca). The requested
|
|
662
895
|
# byte count is rounded up to a multiple of 16 (add 15, then clear the low
|
|
663
896
|
# four bits), sp is lowered by that much, and the resulting sp — the
|
|
@@ -686,92 +919,126 @@ module Rubycc
|
|
|
686
919
|
emit_add_imm(A, A, 15, shift12: false) # add A, A, #15
|
|
687
920
|
emit_word(AND_NOT15 | (A << 5) | A) # and A, A, #-16
|
|
688
921
|
emit_word(SUB_EXTENDED | (A << 16) | (SP << 5) | SP) # sub sp, sp, A
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
922
|
+
# The rounded count in A is dead once sp has moved, so the block's base
|
|
923
|
+
# address goes straight to wherever the destination lives.
|
|
924
|
+
target = result_register(dst)
|
|
925
|
+
emit_add_imm(target, SP, 0, shift12: false) # mov target, sp
|
|
926
|
+
# No `only_wrote:`: the rounding above overwrote A, which the load a
|
|
927
|
+
# line earlier left holding the *requested* count and which any
|
|
928
|
+
# residency in flight may name. Claiming otherwise would leave a reader
|
|
929
|
+
# of the size value believing A still held it (see #store_result).
|
|
930
|
+
store_result(target, dst)
|
|
931
|
+
end
|
|
932
|
+
|
|
933
|
+
# :div/:mod/:udiv/:umod. The quotient is `sdiv`/`udiv` of the dividend by
|
|
934
|
+
# the divisor; a remainder is then dividend - quotient*divisor via
|
|
935
|
+
# `msub`, since AArch64 has no direct remainder instruction. The
|
|
936
|
+
# signed/unsigned split mirrors the IR's own.
|
|
937
|
+
#
|
|
938
|
+
# A remainder needs the quotient as well as the result, so that one keeps
|
|
939
|
+
# the C scratch for it; C is never a promotion register, so the divisor
|
|
940
|
+
# and dividend it is read alongside can be named in place regardless.
|
|
696
941
|
def emit_divmod(dst, a, b, size, signed:, remainder:)
|
|
697
|
-
|
|
698
|
-
load_reg(B, b)
|
|
942
|
+
rn, rm = binary_operand_registers(a, b)
|
|
699
943
|
w = width(size)
|
|
700
944
|
div_base = signed ? (w == 64 ? 0x9AC00C00 : 0x1AC00C00) : (w == 64 ? 0x9AC00800 : 0x1AC00800)
|
|
701
|
-
emit_word(div_base | (B << 16) | (A << 5) | C) # sdiv/udiv C, A, B
|
|
702
945
|
if remainder
|
|
946
|
+
rd = result_register(dst)
|
|
947
|
+
emit_word(div_base | (rm << 16) | (rn << 5) | C) # sdiv/udiv C, a, b
|
|
703
948
|
msub_base = w == 64 ? 0x9B008000 : 0x1B008000
|
|
704
|
-
emit_word(msub_base | (
|
|
705
|
-
|
|
949
|
+
emit_word(msub_base | (rm << 16) | (rn << 10) | (C << 5) | rd) # msub rd, C, b, a
|
|
950
|
+
# No `only_wrote:`: the quotient went to C, which is a scratch a
|
|
951
|
+
# residency may name — the quotient path right below computes into it
|
|
952
|
+
# whenever its destination lives in a slot (see #store_result).
|
|
953
|
+
store_result(rd, dst)
|
|
706
954
|
else
|
|
707
|
-
|
|
955
|
+
rd = result_register(dst, C)
|
|
956
|
+
emit_word(div_base | (rm << 16) | (rn << 5) | rd) # sdiv/udiv rd, a, b
|
|
957
|
+
store_result(rd, dst, only_wrote: rd) # one word, Rd its only written field
|
|
708
958
|
end
|
|
709
959
|
end
|
|
710
960
|
|
|
711
|
-
# :shl/:sar/:shr — a variable shift by
|
|
712
|
-
# a 32-bit operand, 6 for a 64-bit one by the
|
|
961
|
+
# :shl/:sar/:shr — a variable shift by the second operand's low bits
|
|
962
|
+
# (masked to 5 bits for a 32-bit operand, 6 for a 64-bit one by the
|
|
963
|
+
# hardware, matching C). The shift amount is an ordinary Rm here, not the
|
|
964
|
+
# one fixed register x86 makes of it, so a promoted count needs no move.
|
|
713
965
|
def emit_shift(dst, a, b, size, base_table)
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
emit_word(base_table[width(size)] | (
|
|
717
|
-
|
|
966
|
+
rn, rm = binary_operand_registers(a, b)
|
|
967
|
+
rd = result_register(dst)
|
|
968
|
+
emit_word(base_table[width(size)] | (rm << 16) | (rn << 5) | rd)
|
|
969
|
+
store_result(rd, dst, only_wrote: rd) # one word, Rd its only written field
|
|
718
970
|
end
|
|
719
971
|
|
|
720
972
|
# :neg — Rd = -a, encoded as `sub Rd, xzr, a`.
|
|
721
973
|
def emit_neg(dst, src, size)
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
974
|
+
rn = operand_register(src, A)
|
|
975
|
+
rd = result_register(dst)
|
|
976
|
+
emit_word(SUB_SHIFTED[width(size)] | (rn << 16) | (XZR << 5) | rd)
|
|
977
|
+
store_result(rd, dst, only_wrote: rd) # one word, Rd its only written field
|
|
725
978
|
end
|
|
726
979
|
|
|
727
980
|
# A comparison materialized into the destination as an int 0/1. `cmp`
|
|
728
981
|
# sets the flags; `cset` then writes 1 when the condition holds. A size-8
|
|
729
982
|
# comparison uses the 64-bit X view (full pointer values), otherwise the
|
|
730
983
|
# 32-bit W view.
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
984
|
+
#
|
|
985
|
+
# Both ends are read-only fields and `cset`'s destination is free, so a
|
|
986
|
+
# comparison between two promoted values that lands in a third is three
|
|
987
|
+
# registers named in place and no traffic at all.
|
|
988
|
+
def emit_comparison(dst, a, b, condition, size, commutative: false)
|
|
989
|
+
rn, rm = binary_operand_registers(a, b, commutative: commutative)
|
|
990
|
+
rd = result_register(dst)
|
|
991
|
+
emit_word(SUBS_SHIFTED[width(size)] | (rm << 16) | (rn << 5) | XZR) # cmp rn, rm
|
|
992
|
+
emit_cset(rd, condition)
|
|
993
|
+
# The `cmp` writes the flags and register 31, which is xzr here and no
|
|
994
|
+
# register at all; the `cset` writes rd. Nothing the table names.
|
|
995
|
+
store_result(rd, dst, only_wrote: rd)
|
|
737
996
|
end
|
|
738
997
|
|
|
739
998
|
# :sext — sign-extend a's low `size` bytes to the full 64-bit register,
|
|
740
999
|
# so a subsequent 64-bit use (pointer-offset scaling) sees a correct,
|
|
741
1000
|
# possibly negative, value.
|
|
742
1001
|
def emit_sext(dst, src, size)
|
|
743
|
-
|
|
1002
|
+
rn = operand_register(src, A)
|
|
1003
|
+
rd = result_register(dst)
|
|
744
1004
|
word =
|
|
745
1005
|
case size
|
|
746
1006
|
when 1 then 0x93401C00 # sxtb x, w
|
|
747
1007
|
when 2 then 0x93403C00 # sxth x, w
|
|
748
1008
|
else 0x93407C00 # sxtw x, w (size 4)
|
|
749
1009
|
end
|
|
750
|
-
emit_word(word | (
|
|
751
|
-
|
|
1010
|
+
emit_word(word | (rn << 5) | rd)
|
|
1011
|
+
store_result(rd, dst, only_wrote: rd) # one word, Rd its only written field
|
|
752
1012
|
end
|
|
753
1013
|
|
|
754
1014
|
# :zext — zero-extend a's low `size` bytes. The 1/2-byte forms are 32-bit
|
|
755
1015
|
# uxtb/uxth (which zero the upper 32 bits too); size 4 is a 64-bit ubfx of
|
|
756
1016
|
# the low 32 bits, matching x86's "mov eax, eax".
|
|
757
1017
|
def emit_zext(dst, src, size)
|
|
758
|
-
|
|
1018
|
+
rn = operand_register(src, A)
|
|
1019
|
+
rd = result_register(dst)
|
|
759
1020
|
word =
|
|
760
1021
|
case size
|
|
761
1022
|
when 1 then 0x53001C00 # uxtb w, w
|
|
762
1023
|
when 2 then 0x53003C00 # uxth w, w
|
|
763
1024
|
else 0xD3407C00 # ubfx x, x, #0, #32 (size 4)
|
|
764
1025
|
end
|
|
765
|
-
emit_word(word | (
|
|
766
|
-
|
|
1026
|
+
emit_word(word | (rn << 5) | rd)
|
|
1027
|
+
store_result(rd, dst, only_wrote: rd) # one word, Rd its only written field
|
|
767
1028
|
end
|
|
768
1029
|
|
|
769
1030
|
# :load / :uload — read `size` bytes through the pointer in a's slot. A
|
|
770
1031
|
# signed load sign-extends a byte/halfword (ldrsb/ldrsh), the unsigned
|
|
771
1032
|
# form zero-extends (ldrb/ldrh); a 4-byte load is a plain W load (upper
|
|
772
1033
|
# half zeroed) and an 8-byte load a full X load, both sign-agnostic.
|
|
1034
|
+
#
|
|
1035
|
+
# The base register Rn is an ordinary five-bit field, so a promoted
|
|
1036
|
+
# pointer addresses memory where it stands — the extension x86_64's
|
|
1037
|
+
# ModR/M and SIB special cases made worth deferring there and which costs
|
|
1038
|
+
# nothing here.
|
|
773
1039
|
def emit_load(dst, ptr, size, signed:)
|
|
774
|
-
|
|
1040
|
+
rn = operand_register(ptr, A)
|
|
1041
|
+
rd = result_register(dst)
|
|
775
1042
|
word =
|
|
776
1043
|
case size
|
|
777
1044
|
when 8 then 0xF9400000 # ldr x, [x]
|
|
@@ -779,16 +1046,16 @@ module Rubycc
|
|
|
779
1046
|
when 1 then signed ? 0x39C00000 : 0x39400000 # ldrsb/ldrb w, [x]
|
|
780
1047
|
else 0xB9400000 # ldr w, [x] (size 4)
|
|
781
1048
|
end
|
|
782
|
-
emit_word(word | (
|
|
783
|
-
|
|
1049
|
+
emit_word(word | (rn << 5) | rd)
|
|
1050
|
+
store_result(rd, dst, only_wrote: rd) # the load writes Rt; its base is read only
|
|
784
1051
|
end
|
|
785
1052
|
|
|
786
1053
|
# :store — write value b's low `size` bytes through the pointer in a. The
|
|
787
1054
|
# narrower stores (strb/strh/str-w) are exactly the truncation a narrow
|
|
788
|
-
# lvalue needs.
|
|
1055
|
+
# lvalue needs. Base and source register are both free fields, so a
|
|
1056
|
+
# promoted pointer and a promoted value make this one instruction.
|
|
789
1057
|
def emit_store(ptr, value, size)
|
|
790
|
-
|
|
791
|
-
load_reg(B, value) # B = value
|
|
1058
|
+
rn, rt = binary_operand_registers(ptr, value)
|
|
792
1059
|
word =
|
|
793
1060
|
case size
|
|
794
1061
|
when 8 then 0xF9000000 # str x, [x]
|
|
@@ -796,17 +1063,21 @@ module Rubycc
|
|
|
796
1063
|
when 1 then 0x39000000 # strb w, [x]
|
|
797
1064
|
else 0xB9000000 # str w, [x] (size 4)
|
|
798
1065
|
end
|
|
799
|
-
emit_word(word | (
|
|
1066
|
+
emit_word(word | (rn << 5) | rt)
|
|
800
1067
|
end
|
|
801
1068
|
|
|
802
1069
|
# :jump_if_zero — branch when a's low 32 bits are zero. Testing the W view
|
|
803
1070
|
# (cbz w) mirrors the x86_64 backend's 32-bit "test eax, eax": the
|
|
804
1071
|
# condition is an int 0/1 or a truthiness test the generator has already
|
|
805
1072
|
# reduced.
|
|
1073
|
+
#
|
|
1074
|
+
# CBZ names the register it tests in a free field, so a promoted
|
|
1075
|
+
# condition is branched on where it lives and the loop's test costs one
|
|
1076
|
+
# instruction.
|
|
806
1077
|
def emit_jump_if_zero(cond, label_id)
|
|
807
|
-
|
|
1078
|
+
rt = operand_register(cond, A)
|
|
808
1079
|
@fixups << [@code.bytesize, label_id, :cbz]
|
|
809
|
-
emit_word(0x34000000 |
|
|
1080
|
+
emit_word(0x34000000 | rt) # cbz w{rt}, <patched>
|
|
810
1081
|
end
|
|
811
1082
|
|
|
812
1083
|
# :call — a direct call. Arguments are placed in x0..x7 / v0..v7, then `bl`
|
|
@@ -944,6 +1215,17 @@ module Rubycc
|
|
|
944
1215
|
|
|
945
1216
|
load_fp(FP_ARG_REGISTERS[next_fp], vreg, kind == :sse8 ? 8 : 4)
|
|
946
1217
|
next_fp += 1
|
|
1218
|
+
when :sse16
|
|
1219
|
+
raise "call argument #{kind} overruns the vector registers" if next_fp >= FP_ARG_REGISTERS.size
|
|
1220
|
+
|
|
1221
|
+
# A quad-precision argument (AAPCS64's `long double`) fills a whole
|
|
1222
|
+
# vector register, which no eightbyte slot could have held: its
|
|
1223
|
+
# vreg carries the *address* of the 16-byte value instead, and the
|
|
1224
|
+
# register is loaded from there. A is free here — the stack pass
|
|
1225
|
+
# above has finished with it and no argument register is A.
|
|
1226
|
+
load_reg(A, vreg)
|
|
1227
|
+
emit_word(LDR_Q | (A << 5) | FP_ARG_REGISTERS[next_fp]) # ldr q, [A]
|
|
1228
|
+
next_fp += 1
|
|
947
1229
|
when :indirect_result
|
|
948
1230
|
# The result buffer's address goes in x8, outside both argument
|
|
949
1231
|
# sequences, so it never displaces a real argument.
|
|
@@ -1024,8 +1306,10 @@ module Rubycc
|
|
|
1024
1306
|
# flight. None is an argument register, so a copy made while a call's
|
|
1025
1307
|
# arguments are being prepared never disturbs one already placed.
|
|
1026
1308
|
def emit_memcpy(dest_vreg, src_vreg, byte_count)
|
|
1027
|
-
|
|
1028
|
-
|
|
1309
|
+
# Through #load_binary_operands rather than two bare loads: the source
|
|
1310
|
+
# address may be a transient waiting in A, which filling A with the
|
|
1311
|
+
# destination would destroy for good (its slot was never written).
|
|
1312
|
+
load_binary_operands(dest_vreg, src_vreg) # A = destination, B = source
|
|
1029
1313
|
chunks = byte_count / 8
|
|
1030
1314
|
if chunks > MEMCPY_UNROLL_LIMIT
|
|
1031
1315
|
emit_memcpy_loop(chunks)
|
|
@@ -1231,13 +1515,216 @@ module Rubycc
|
|
|
1231
1515
|
# is never truncated; a 32-bit value's high half was zeroed when it was
|
|
1232
1516
|
# produced. Reaches the slot through the scaled immediate when it fits,
|
|
1233
1517
|
# otherwise through a composed address in the ADDR scratch.
|
|
1518
|
+
#
|
|
1519
|
+
# The load disappears when `reg` already holds this slot's value and
|
|
1520
|
+
# becomes a register move when another scratch register does (see
|
|
1521
|
+
# SlotResidency). ADDR is never tracked, so the address composition the
|
|
1522
|
+
# distant-slot path performs in it cannot leave a stale claim behind: it
|
|
1523
|
+
# is a scratch this method writes and reads within one instruction and
|
|
1524
|
+
# #load_reg is never asked to fill it.
|
|
1234
1525
|
def load_reg(reg, vreg)
|
|
1235
|
-
|
|
1526
|
+
refresh_slot_residency
|
|
1527
|
+
promoted = @promoted[vreg]
|
|
1528
|
+
if promoted
|
|
1529
|
+
# There is no slot to read: this value has been in `promoted` since
|
|
1530
|
+
# the prologue and stays there until the function returns, so the
|
|
1531
|
+
# load is a register move — or nothing at all when the caller wants
|
|
1532
|
+
# it where it already is. Either way only `reg` is written, which is
|
|
1533
|
+
# what the residency table has to be told.
|
|
1534
|
+
emit_move_register(reg, promoted) unless reg == promoted
|
|
1535
|
+
note_register_clobbered(reg)
|
|
1536
|
+
return
|
|
1537
|
+
end
|
|
1538
|
+
return if slot_resident_in?(reg, vreg)
|
|
1539
|
+
|
|
1540
|
+
source = register_holding_slot(vreg)
|
|
1541
|
+
if source
|
|
1542
|
+
emit_move_register(reg, source)
|
|
1543
|
+
else
|
|
1544
|
+
load_frame_at(reg, slot_offset(vreg))
|
|
1545
|
+
end
|
|
1546
|
+
if PROMOTION_REGISTER_SET.include?(reg)
|
|
1547
|
+
# Filling a promoted register from some *other* value's slot happens
|
|
1548
|
+
# where that register is a result destination and the value is on its
|
|
1549
|
+
# way in (#emit_copy, a stack parameter). Recording a residency
|
|
1550
|
+
# against it would break the invariant every note_register_clobbered
|
|
1551
|
+
# in this backend rests on — that no entry in the table is keyed by a
|
|
1552
|
+
# promotion register — and would be short-lived anyway, the result
|
|
1553
|
+
# being written over it in the next instruction.
|
|
1554
|
+
note_register_clobbered(reg)
|
|
1555
|
+
else
|
|
1556
|
+
note_slot_loaded(reg, vreg)
|
|
1557
|
+
end
|
|
1558
|
+
end
|
|
1559
|
+
|
|
1560
|
+
# mov Xd, Xn, which the architecture spells as an ORR with the zero
|
|
1561
|
+
# register.
|
|
1562
|
+
def emit_move_register(dst, src)
|
|
1563
|
+
emit_word(ORR_SHIFTED[64] | (src << 16) | (XZR << 5) | dst)
|
|
1236
1564
|
end
|
|
1237
1565
|
|
|
1238
|
-
# str X{reg}, [slot]. The counterpart of #load_reg.
|
|
1566
|
+
# str X{reg}, [slot]. The counterpart of #load_reg. The store itself is
|
|
1567
|
+
# never skipped: the slot is the value's home, and nothing here knows
|
|
1568
|
+
# whether a later branch reaches a reader by a path that goes nowhere near
|
|
1569
|
+
# this register.
|
|
1239
1570
|
def store_reg(reg, vreg)
|
|
1571
|
+
refresh_slot_residency
|
|
1572
|
+
promoted = @promoted[vreg]
|
|
1573
|
+
if promoted
|
|
1574
|
+
# The value's home is a register, so the store is a move into it —
|
|
1575
|
+
# and nothing at all when the value was computed there. No slot is
|
|
1576
|
+
# written and no residency can name a promotion register, so every
|
|
1577
|
+
# entry in the table is still true.
|
|
1578
|
+
emit_move_register(promoted, reg) unless reg == promoted
|
|
1579
|
+
note_slots_undisturbed
|
|
1580
|
+
return
|
|
1581
|
+
end
|
|
1582
|
+
if @transient[vreg]
|
|
1583
|
+
# The value's only reader is the next instruction, which will find it
|
|
1584
|
+
# here; the slot itself is never named again, so nothing is written.
|
|
1585
|
+
note_slot_loaded(reg, vreg)
|
|
1586
|
+
return
|
|
1587
|
+
end
|
|
1240
1588
|
store_frame_at(reg, slot_offset(vreg))
|
|
1589
|
+
note_slot_stored(reg, vreg)
|
|
1590
|
+
end
|
|
1591
|
+
|
|
1592
|
+
# Loads a two-operand instruction's operands into A and B.
|
|
1593
|
+
#
|
|
1594
|
+
# Which one is fetched first stops being arbitrary once #load_reg can
|
|
1595
|
+
# reuse a resident value: if `b` is the value sitting in A, loading `a`
|
|
1596
|
+
# there first would throw it away and force `b` back out of memory.
|
|
1597
|
+
# Fetching B first instead keeps it, as a register move. A `commutative`
|
|
1598
|
+
# op can do better still — swapping the two makes the resident operand A's,
|
|
1599
|
+
# so nothing is moved at all — and is safe to swap precisely because the
|
|
1600
|
+
# instruction's two register operands are interchangeable (which is not
|
|
1601
|
+
# true of :sub, of the shifts, or of an ordering comparison).
|
|
1602
|
+
def load_binary_operands(a, b, commutative: false)
|
|
1603
|
+
refresh_slot_residency
|
|
1604
|
+
if slot_resident_in?(A, b) && !slot_resident_in?(A, a)
|
|
1605
|
+
if commutative
|
|
1606
|
+
a, b = b, a
|
|
1607
|
+
else
|
|
1608
|
+
load_reg(B, b)
|
|
1609
|
+
end
|
|
1610
|
+
end
|
|
1611
|
+
load_reg(A, a)
|
|
1612
|
+
load_reg(B, b)
|
|
1613
|
+
end
|
|
1614
|
+
|
|
1615
|
+
# --- naming promoted registers in place -------------------------------
|
|
1616
|
+
#
|
|
1617
|
+
# AArch64's data-processing instructions are three-address — Rd, Rn and
|
|
1618
|
+
# Rm are independent five-bit fields naming any of the 31 general
|
|
1619
|
+
# registers — and its loads and stores name their base register just as
|
|
1620
|
+
# freely. So a promoted value is used *where it lives*, both as an
|
|
1621
|
+
# operand and as a destination, and a promoted addition really is one
|
|
1622
|
+
# instruction: `add x19, x19, x20`.
|
|
1623
|
+
#
|
|
1624
|
+
# That is the whole difference from the x86_64 backend, where the same
|
|
1625
|
+
# step had to be split in two (put the value in a register, then teach
|
|
1626
|
+
# each encoding to name it) because a two-address machine with one
|
|
1627
|
+
# memory operand cannot express the second half for free. The measured
|
|
1628
|
+
# consequence there was that promotion *alone* made code slower: every
|
|
1629
|
+
# memory operand it removed became two register instructions. There is no
|
|
1630
|
+
# such half-way state here.
|
|
1631
|
+
#
|
|
1632
|
+
# What is deliberately not widened is everything the ABI pins down: a
|
|
1633
|
+
# call's arguments (x0..x7, v0..v7), its result (x0/v0), the atomic
|
|
1634
|
+
# sequences' exclusive pair and the whole-object copy, whose loop
|
|
1635
|
+
# advances the addresses it was handed and so must be given scratch
|
|
1636
|
+
# registers of its own.
|
|
1637
|
+
|
|
1638
|
+
# The register an instruction should read `vreg` out of: the callee-saved
|
|
1639
|
+
# register it was promoted into, a scratch register that already holds
|
|
1640
|
+
# the value, or `scratch` with the value loaded into it.
|
|
1641
|
+
#
|
|
1642
|
+
# The middle case is what keeps a transient free. #load_binary_operands
|
|
1643
|
+
# has to move one out of A when the *other* operand is on its way in
|
|
1644
|
+
# there; where the other operand is promoted nothing is on its way in at
|
|
1645
|
+
# all, so the value is read where its producer left it.
|
|
1646
|
+
#
|
|
1647
|
+
# Only A and B answer, though a residency may name any scratch register:
|
|
1648
|
+
# those two are the ones nothing writes between the operands being named
|
|
1649
|
+
# and the last instruction that reads them, while C is written in the
|
|
1650
|
+
# middle of a remainder (it takes the quotient) and could equally be
|
|
1651
|
+
# holding one of the values that remainder is about to read.
|
|
1652
|
+
def operand_register(vreg, scratch)
|
|
1653
|
+
promoted = @promoted[vreg]
|
|
1654
|
+
return promoted if promoted
|
|
1655
|
+
|
|
1656
|
+
refresh_slot_residency
|
|
1657
|
+
return A if slot_resident_in?(A, vreg)
|
|
1658
|
+
return B if slot_resident_in?(B, vreg)
|
|
1659
|
+
|
|
1660
|
+
load_reg(scratch, vreg)
|
|
1661
|
+
scratch
|
|
1662
|
+
end
|
|
1663
|
+
|
|
1664
|
+
# The pair of registers a two-operand instruction should read, one per
|
|
1665
|
+
# operand. A promoted operand is named where it is; the rest go through
|
|
1666
|
+
# A and B as before, and when neither operand is promoted this is exactly
|
|
1667
|
+
# #load_binary_operands — including its rescue of an operand still
|
|
1668
|
+
# sitting in A, which only arises when something has to be loaded there.
|
|
1669
|
+
#
|
|
1670
|
+
# `commutative` is passed on for that rescue's sake alone. Nothing here
|
|
1671
|
+
# needs the two exchanged the way the x86_64 backend does to reach an
|
|
1672
|
+
# in-place form: this machine's destination field is free, so
|
|
1673
|
+
# `sub x19, x20, x19` and `sub x19, x19, x20` cost the same one
|
|
1674
|
+
# instruction and neither reads a register the other has overwritten —
|
|
1675
|
+
# every source is read before Rd is written.
|
|
1676
|
+
def binary_operand_registers(a, b, commutative: false)
|
|
1677
|
+
promoted_a = @promoted[a]
|
|
1678
|
+
promoted_b = @promoted[b]
|
|
1679
|
+
return [promoted_a, promoted_b] if promoted_a && promoted_b
|
|
1680
|
+
return [promoted_a, operand_register(b, B)] if promoted_a
|
|
1681
|
+
return [operand_register(a, A), promoted_b] if promoted_b
|
|
1682
|
+
|
|
1683
|
+
load_binary_operands(a, b, commutative: commutative)
|
|
1684
|
+
[A, B]
|
|
1685
|
+
end
|
|
1686
|
+
|
|
1687
|
+
# Where an instruction that computes a general-register result should put
|
|
1688
|
+
# it: the callee-saved register `dst` was promoted into, so the value is
|
|
1689
|
+
# written home directly, or `scratch` for a value that lives in a slot.
|
|
1690
|
+
def result_register(dst, scratch = A)
|
|
1691
|
+
@promoted[dst] || scratch
|
|
1692
|
+
end
|
|
1693
|
+
|
|
1694
|
+
# Completes such an instruction. A destination that lives in a slot still
|
|
1695
|
+
# needs the store; a promoted `dst` *is* `reg`, so the value is already
|
|
1696
|
+
# home and all that is left is telling the residency table what the bytes
|
|
1697
|
+
# just emitted did to it.
|
|
1698
|
+
#
|
|
1699
|
+
# **The default answer is to throw the table away.** Bytes have been
|
|
1700
|
+
# emitted since it was last known true — the caller's own instruction —
|
|
1701
|
+
# and nothing here can see which registers they wrote. Keeping the table
|
|
1702
|
+
# alive across them is #note_register_clobbered's exception, sound only
|
|
1703
|
+
# when those bytes wrote `reg` and nothing else the table can name; that
|
|
1704
|
+
# is a fact about one lowering's encoding, so the lowering is what states
|
|
1705
|
+
# it, by passing `only_wrote: reg`. Anything that says nothing gets the
|
|
1706
|
+
# safe reading, which costs an optimization and never correctness — and a
|
|
1707
|
+
# lowering that later grows a second scratch write has to come back to its
|
|
1708
|
+
# own claim rather than silently reviving a stale residency. That is the
|
|
1709
|
+
# shape #emit_alloca and #emit_divmod were caught in: both wrote A or C on
|
|
1710
|
+
# the way to the result and both revalidated the table on the way out.
|
|
1711
|
+
#
|
|
1712
|
+
# "Nothing else the table can name" means none of A, B, C or x0..x8, which
|
|
1713
|
+
# are the registers #load_reg is ever asked to fill and so the only ones an
|
|
1714
|
+
# entry can be keyed by. Writing sp, ADDR, x13..x15 or a promotion register
|
|
1715
|
+
# does not disturb the table (see PROMOTION_REGISTERS and #load_reg), so it
|
|
1716
|
+
# does not stop a caller from claiming the fast path.
|
|
1717
|
+
def store_result(reg, dst, only_wrote: nil)
|
|
1718
|
+
unless @promoted[dst]
|
|
1719
|
+
store_reg(reg, dst) # which discards a stale table itself, before anything else
|
|
1720
|
+
return
|
|
1721
|
+
end
|
|
1722
|
+
|
|
1723
|
+
if only_wrote == reg
|
|
1724
|
+
note_register_clobbered(reg)
|
|
1725
|
+
else
|
|
1726
|
+
refresh_slot_residency
|
|
1727
|
+
end
|
|
1241
1728
|
end
|
|
1242
1729
|
|
|
1243
1730
|
# ldr X{reg}, [frame base + offset] for any fixed-frame offset, not just a
|
|
@@ -1294,13 +1781,49 @@ module Rubycc
|
|
|
1294
1781
|
# extra instructions per operand and buy nothing, since ldr/str address a
|
|
1295
1782
|
# V register with the same sp-relative form the X registers use — the sole
|
|
1296
1783
|
# difference being the V bit that selects the register file.
|
|
1784
|
+
#
|
|
1785
|
+
# As with #load_reg, the move disappears when `reg` already holds this
|
|
1786
|
+
# slot at this width and becomes a register-to-register `fmov` when
|
|
1787
|
+
# another vector register does — which copies exactly the bits a reload
|
|
1788
|
+
# would have produced at that width.
|
|
1789
|
+
#
|
|
1790
|
+
# Unlike #load_reg this asks nothing about promotion: a value the vector
|
|
1791
|
+
# register file touches is refused promotion outright (IR::Promotion's
|
|
1792
|
+
# VECTOR_OPS and VECTOR_KINDS), so every vreg reaching here has a slot.
|
|
1297
1793
|
def load_fp(reg, vreg, size)
|
|
1298
|
-
|
|
1794
|
+
refresh_slot_residency
|
|
1795
|
+
return if slot_resident_in_vector?(reg, vreg, size)
|
|
1796
|
+
|
|
1797
|
+
source = vector_register_holding_slot(vreg, size)
|
|
1798
|
+
if source
|
|
1799
|
+
emit_word(FMOV_REG.fetch(size) | (source << 5) | reg)
|
|
1800
|
+
else
|
|
1801
|
+
emit_fp_slot_access(reg, slot_offset(vreg), size, load: true)
|
|
1802
|
+
end
|
|
1803
|
+
note_slot_loaded_to_vector(reg, vreg, size)
|
|
1804
|
+
end
|
|
1805
|
+
|
|
1806
|
+
# Stages a floating instruction's operands into FA and FB. When the second
|
|
1807
|
+
# was never written to its slot (a transient waiting in FA), it is rescued
|
|
1808
|
+
# into FB before the first operand lands on top of it — the vector-file
|
|
1809
|
+
# version of the ordering rule #load_binary_operands follows.
|
|
1810
|
+
def load_float_operands(a, b, size)
|
|
1811
|
+
refresh_slot_residency
|
|
1812
|
+
load_fp(FB, b, size) if slot_resident_in_vector?(FA, b, size) &&
|
|
1813
|
+
!slot_resident_in_vector?(FA, a, size)
|
|
1814
|
+
load_fp(FA, a, size)
|
|
1815
|
+
load_fp(FB, b, size)
|
|
1299
1816
|
end
|
|
1300
1817
|
|
|
1301
1818
|
# str S{reg}/D{reg}, [slot]. The counterpart of #load_fp.
|
|
1302
1819
|
def store_fp(reg, vreg, size)
|
|
1820
|
+
refresh_slot_residency
|
|
1821
|
+
if @transient[vreg]
|
|
1822
|
+
note_slot_loaded_to_vector(reg, vreg, size)
|
|
1823
|
+
return
|
|
1824
|
+
end
|
|
1303
1825
|
emit_fp_slot_access(reg, slot_offset(vreg), size, load: false)
|
|
1826
|
+
note_slot_stored_from_vector(reg, vreg, size)
|
|
1304
1827
|
end
|
|
1305
1828
|
|
|
1306
1829
|
# The shared body of #load_fp / #store_fp. The unsigned-offset immediate is
|
|
@@ -1326,8 +1849,7 @@ module Rubycc
|
|
|
1326
1849
|
# sign bit with an integer :xor, which the integer path already lowers.
|
|
1327
1850
|
def emit_float_binary(inst, base_table)
|
|
1328
1851
|
size = inst.size
|
|
1329
|
-
|
|
1330
|
-
load_fp(FB, inst.b, size)
|
|
1852
|
+
load_float_operands(inst.a, inst.b, size)
|
|
1331
1853
|
emit_word(base_table.fetch(size) | (FB << 16) | (FA << 5) | FA)
|
|
1332
1854
|
store_fp(FA, inst.dst, size)
|
|
1333
1855
|
end
|
|
@@ -1338,8 +1860,7 @@ module Rubycc
|
|
|
1338
1860
|
# FLOAT_CONDITIONS). The `cset` is the 32-bit form, so the slot gets a
|
|
1339
1861
|
# clean int with its upper half zeroed.
|
|
1340
1862
|
def emit_float_comparison(dst, a, b, condition, size)
|
|
1341
|
-
|
|
1342
|
-
load_fp(FB, b, size)
|
|
1863
|
+
load_float_operands(a, b, size)
|
|
1343
1864
|
emit_word(FCMP.fetch(size) | (FB << 16) | (FA << 5)) # fcmp FA, FB
|
|
1344
1865
|
emit_cset(A, condition)
|
|
1345
1866
|
store_reg(A, dst)
|
|
@@ -1416,10 +1937,11 @@ module Rubycc
|
|
|
1416
1937
|
# finally loaded — but it does bind the symbol at link time, which is what
|
|
1417
1938
|
# separates it from the GOT path below.
|
|
1418
1939
|
def emit_symbol_address(dst, reloc)
|
|
1940
|
+
target = result_register(dst)
|
|
1419
1941
|
@relocations << reloc.merge(offset: @code.bytesize)
|
|
1420
|
-
emit_word(0x90000000 |
|
|
1421
|
-
emit_add_imm(
|
|
1422
|
-
|
|
1942
|
+
emit_word(0x90000000 | target) # adrp target, <page of sym>
|
|
1943
|
+
emit_add_imm(target, target, 0, shift12: false) # add target, target, #:lo12:sym
|
|
1944
|
+
store_result(target, dst, only_wrote: target) # the pair writes target alone
|
|
1423
1945
|
end
|
|
1424
1946
|
|
|
1425
1947
|
# :got_addr — the PIC form of the above. Under -fPIC a symbol this unit
|
|
@@ -1432,17 +1954,21 @@ module Rubycc
|
|
|
1432
1954
|
# value that lands in the destination slot is a usable pointer either way
|
|
1433
1955
|
# and every load or store through it is unchanged.
|
|
1434
1956
|
def emit_got_address(dst, symbol)
|
|
1957
|
+
target = result_register(dst)
|
|
1435
1958
|
@relocations << { kind: :got, offset: @code.bytesize, symbol: symbol }
|
|
1436
|
-
emit_word(0x90000000 |
|
|
1437
|
-
emit_word(0xF9400000 | (
|
|
1438
|
-
|
|
1959
|
+
emit_word(0x90000000 | target) # adrp target, <page of GOT slot>
|
|
1960
|
+
emit_word(0xF9400000 | (target << 5) | target) # ldr target, [target, #:got_lo12:]
|
|
1961
|
+
store_result(target, dst, only_wrote: target) # the pair writes target alone
|
|
1439
1962
|
end
|
|
1440
1963
|
|
|
1441
1964
|
# :addr_of / :object_addr — compute a frame address (sp + offset) into a
|
|
1442
1965
|
# scratch register and park it in the destination slot as a pointer value.
|
|
1443
1966
|
def emit_slot_address_to(dst, offset)
|
|
1444
|
-
|
|
1445
|
-
|
|
1967
|
+
target = result_register(dst)
|
|
1968
|
+
emit_slot_address(target, offset)
|
|
1969
|
+
# However far the offset reaches, the composition builds it in target
|
|
1970
|
+
# and reads only the frame base (#emit_base_address).
|
|
1971
|
+
store_result(target, dst, only_wrote: target)
|
|
1446
1972
|
end
|
|
1447
1973
|
|
|
1448
1974
|
# Places (frame base) + offset (offset >= 0) into `reg`, the address of a
|
|
@@ -1602,6 +2128,12 @@ module Rubycc
|
|
|
1602
2128
|
LSRV = { 32 => 0x1AC02400, 64 => 0x9AC02400 }.freeze
|
|
1603
2129
|
ASRV = { 32 => 0x1AC02800, 64 => 0x9AC02800 }.freeze
|
|
1604
2130
|
|
|
2131
|
+
# :scaled_add's element size -> the imm6 shift amount the shifted-register
|
|
2132
|
+
# add above applies to its second operand (the shift type field stays 00,
|
|
2133
|
+
# LSL). These four powers of two are exactly the set IR::Simplify fuses a
|
|
2134
|
+
# subscript for.
|
|
2135
|
+
SHIFTED_SCALES = { 1 => 0, 2 => 1, 4 => 2, 8 => 3 }.freeze
|
|
2136
|
+
|
|
1605
2137
|
# umulh Rd, Rn, Rm — a "data-processing (3 source)" instruction in the
|
|
1606
2138
|
# same family as madd/msub, with op31 = 110 selecting the unsigned-high
|
|
1607
2139
|
# variant and Ra fixed to xzr (31) in bits [14:10]. Only the 64-bit (sf=1)
|
|
@@ -1618,6 +2150,49 @@ module Rubycc
|
|
|
1618
2150
|
RBIT = { 32 => 0x5AC00000, 64 => 0xDAC00000 }.freeze
|
|
1619
2151
|
CLZ = { 32 => 0x5AC01000, 64 => 0xDAC01000 }.freeze
|
|
1620
2152
|
|
|
2153
|
+
# madd Rd, Rn, Rm, xzr — the plain multiply, which the architecture
|
|
2154
|
+
# spells as a multiply-add whose addend is the zero register. Both :mul
|
|
2155
|
+
# and the population count's last stage need it, at either width.
|
|
2156
|
+
MADD_ZERO = { 32 => 0x1B007C00, 64 => 0x9B007C00 }.freeze
|
|
2157
|
+
|
|
2158
|
+
# "Bitfield" (UBFM) and "Logical (immediate)", the two families the
|
|
2159
|
+
# population count expands into:
|
|
2160
|
+
# sf(31) opc(30:29) 100110(28:23) N(22) immr(21:16) imms(15:10) Rn Rd
|
|
2161
|
+
# -- UBFM, opc = 10
|
|
2162
|
+
# sf(31) opc(30:29) 100100(28:23) N(22) immr(21:16) imms(15:10) Rn Rd
|
|
2163
|
+
# -- AND/ORR immediate, opc =
|
|
2164
|
+
# 00 (AND) and 01 (ORR)
|
|
2165
|
+
# A shift by an immediate has no opcode of its own: `lsr Rd, Rn, #s` is
|
|
2166
|
+
# UBFM with immr = s and imms = the register's top bit (31 or 63), which
|
|
2167
|
+
# extracts the bits from s upwards and right-aligns them. Its 64-bit form
|
|
2168
|
+
# sets N = 1 (a 64-bit bitfield), which is the 0x400000 in that base; the
|
|
2169
|
+
# two logical bases leave N = 0, every pattern the count needs being
|
|
2170
|
+
# shorter than 32 bits (below).
|
|
2171
|
+
LSR_IMM = { 32 => 0x53000000, 64 => 0xD3400000 }.freeze
|
|
2172
|
+
AND_IMM = { 32 => 0x12000000, 64 => 0x92000000 }.freeze
|
|
2173
|
+
MOV_IMM = { 32 => 0x32000000, 64 => 0xB2000000 }.freeze # orr Rd, xzr, #imm
|
|
2174
|
+
|
|
2175
|
+
# The masks and the multiplier of the population count, as their imms
|
|
2176
|
+
# fields. A logical immediate is a pattern of `esize` bits — 2, 4, 8, 16,
|
|
2177
|
+
# 32 or 64 — repeated to fill the register, holding a run of ones that
|
|
2178
|
+
# immr then rotates; N:imms names the element size and the run length
|
|
2179
|
+
# together, by how many leading ones imms carries:
|
|
2180
|
+
#
|
|
2181
|
+
# 0b11110s esize 2, s + 1 ones 0x5555... s = 0: one 1 per pair
|
|
2182
|
+
# 0b1110ss esize 4, s + 1 ones 0x3333... s = 1: two 1s per nibble
|
|
2183
|
+
# 0b110sss esize 8, s + 1 ones 0x0F0F... s = 3: four 1s per byte
|
|
2184
|
+
# 0x0101... s = 0: one 1 per byte
|
|
2185
|
+
#
|
|
2186
|
+
# All four runs start at bit 0, so immr is 0 for each and the same field
|
|
2187
|
+
# serves either width (N = 0 in both, the pattern being shorter than 32
|
|
2188
|
+
# bits). This is why the expansion needs no movz/movk anywhere: the
|
|
2189
|
+
# constants it works with are precisely the repeating kind this encoding
|
|
2190
|
+
# was made for.
|
|
2191
|
+
POPCOUNT_PAIR_IMMS = 0b111100
|
|
2192
|
+
POPCOUNT_NIBBLE_IMMS = 0b111001
|
|
2193
|
+
POPCOUNT_BYTE_IMMS = 0b110011
|
|
2194
|
+
POPCOUNT_ONES_IMMS = 0b110000
|
|
2195
|
+
|
|
1621
2196
|
# "Add/subtract (extended register)", 64-bit, with option = 011 (UXTX, the
|
|
1622
2197
|
# identity extension of an X operand) and no shift:
|
|
1623
2198
|
# sf(31)=1 op(30) S(29)=0 01011(28:24) opt(23:22)=00 1(21) Rm(20:16)
|
|
@@ -1698,6 +2273,19 @@ module Rubycc
|
|
|
1698
2273
|
8 => { load: 0xFD400000, store: 0xFD000000 }
|
|
1699
2274
|
}.freeze
|
|
1700
2275
|
|
|
2276
|
+
# The same form at the 128-bit width, which the encoding reaches through
|
|
2277
|
+
# the opc field rather than the size field: size = 00 with opc = 11 is
|
|
2278
|
+
# "ldr q, [Xn]". Only the zero-offset load is needed — a quad-precision
|
|
2279
|
+
# argument is read once from the object the generator built it in — so
|
|
2280
|
+
# this is the bare instruction with an empty imm12.
|
|
2281
|
+
LDR_Q = 0x3DC00000
|
|
2282
|
+
|
|
2283
|
+
# "fmov Sd, Sn" / "fmov Dd, Dn", the vector-file register move, keyed by
|
|
2284
|
+
# the value's width — which is also the encoding's type field (00 single,
|
|
2285
|
+
# 01 double). Used where a floating value wanted in one scratch register
|
|
2286
|
+
# is already in the other.
|
|
2287
|
+
FMOV_REG = { 4 => 0x1E204000, 8 => 0x1E604000 }.freeze
|
|
2288
|
+
|
|
1701
2289
|
# The same form with V = 0, the general-purpose register file, at each of
|
|
1702
2290
|
# the four access widths. The narrow loads are the zero-extending ones
|
|
1703
2291
|
# (ldrb/ldrh rather than ldrsb/ldrsh): both a whole-object copy and an
|