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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +61 -0
  3. data/README.md +26 -14
  4. data/data/verified_gems.json +85 -63
  5. data/exe/rubycc-ar +11 -3
  6. data/include/libc/sys/cdefs.h +12 -0
  7. data/lib/rubycc/backend/aarch64.rb +705 -117
  8. data/lib/rubycc/backend/slot_residency.rb +169 -0
  9. data/lib/rubycc/backend/x86_64.rb +924 -137
  10. data/lib/rubycc/command_line.rb +339 -0
  11. data/lib/rubycc/compile_error.rb +6 -3
  12. data/lib/rubycc/compiler.rb +17 -2
  13. data/lib/rubycc/diagnostics.rb +105 -0
  14. data/lib/rubycc/doctor/gemfile.rb +12 -3
  15. data/lib/rubycc/doctor/verified_gems.rb +5 -1
  16. data/lib/rubycc/driver.rb +66 -10
  17. data/lib/rubycc/front/ast.rb +18 -7
  18. data/lib/rubycc/front/constant_evaluator.rb +12 -0
  19. data/lib/rubycc/front/lexeme_reader.rb +3 -1
  20. data/lib/rubycc/front/parser.rb +51 -18
  21. data/lib/rubycc/ir/analysis.rb +82 -0
  22. data/lib/rubycc/ir/call_convention.rb +74 -7
  23. data/lib/rubycc/ir/generator.rb +319 -9
  24. data/lib/rubycc/ir/ir.rb +39 -1
  25. data/lib/rubycc/ir/promotion.rb +255 -0
  26. data/lib/rubycc/ir/simplify.rb +570 -0
  27. data/lib/rubycc/link/library_resolver.rb +17 -5
  28. data/lib/rubycc/link/partial_linker.rb +8 -1
  29. data/lib/rubycc/link/shared_linker.rb +2 -2
  30. data/lib/rubycc/mkmf_shim.rb +178 -12
  31. data/lib/rubycc/objfile/ar_archive.rb +13 -2
  32. data/lib/rubycc/objfile/elf_reader.rb +13 -2
  33. data/lib/rubycc/pkgconf/parser.rb +4 -0
  34. data/lib/rubycc/pkgconf/resolver.rb +3 -1
  35. data/lib/rubycc/pkgconf/system_path_filter.rb +8 -2
  36. data/lib/rubycc/preprocess/preprocessor.rb +161 -37
  37. data/lib/rubycc/preprocess/scanner.rb +69 -14
  38. data/lib/rubycc/preprocess/token_converter.rb +11 -1
  39. data/lib/rubycc/rmake/cli.rb +32 -4
  40. data/lib/rubycc/rmake/executor.rb +157 -226
  41. data/lib/rubycc/rmake/makefile.rb +35 -13
  42. data/lib/rubycc/rmake/parser.rb +8 -2
  43. data/lib/rubycc/rmake/rmake.rb +1 -0
  44. data/lib/rubycc/rmake/tool_command.rb +69 -0
  45. data/lib/rubycc/shell.rb +510 -0
  46. data/lib/rubycc/type.rb +23 -7
  47. data/lib/rubycc/version.rb +1 -1
  48. data/lib/rubycc.rb +12 -0
  49. data/lib/rubygems_plugin.rb +31 -3
  50. 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 every virtual register owns an 8-byte stack
24
- # slot and each IR instruction loads its operands into scratch registers,
25
- # computes, and stores the result back — but the machine underneath is
26
- # entirely different: fixed-length 32-bit instructions, a flat register
27
- # file, and load/store addressing that shapes the frame layout.
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
- # and the stack objects. A slot whose offset still overflows the scaled
37
- # immediate is reached by composing its address into a scratch register with
38
- # add-immediate(s) — the path is built in from the start rather than bolted
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. The generator
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
- def compile(ir_func)
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. All offsets are non-negative displacements from the frame
291
- # base, which this method also decides (see #frame_base_register).
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 parameter slot so it reads back
474
- # like any other vreg. Integer/pointer parameters come out of x0..x7 and
475
- # floating ones out of v0..v7, each sequence advancing its own counter; a
476
- # :mem parameter is copied down from the caller's stack argument area
477
- # (through the A scratch, which is not an argument register, so the copies
478
- # never disturb an argument still to be spilled) as a whole eightbyte,
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
- load_frame_at(A, incoming_stack_offset(next_stack))
495
- store_reg(A, i)
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 then @labels[inst.a] = @code.bytesize
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
- materialize(A, value, size == 8 ? 64 : 32)
589
- store_reg(A, dst)
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 slot-to-slot transfer.
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
- load_reg(A, src)
595
- store_reg(A, dst)
596
- end
597
-
598
- # An arithmetic/logical binary op. The operands are loaded into A/B and
599
- # combined with a shifted-register instruction whose width follows the IR
600
- # size (64-bit for size 8, otherwise a 32-bit W-register op whose upper
601
- # half is zeroed for free).
602
- def emit_arith(inst, base_table)
603
- load_reg(A, inst.a)
604
- load_reg(B, inst.b)
605
- emit_word(base_table[width(inst.size)] | (B << 16) | (A << 5) | A)
606
- store_reg(A, inst.dst)
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
- load_reg(A, a)
612
- load_reg(B, b)
613
- base = size == 8 ? 0x9B007C00 : 0x1B007C00 # madd with Ra = xzr
614
- emit_word(base | (B << 16) | (A << 5) | A)
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
- load_reg(A, a)
627
- load_reg(B, b)
628
- emit_word(UMULH | (B << 16) | (A << 5) | A)
629
- store_reg(A, dst)
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
- load_reg(A, src_vreg)
656
- emit_word(RBIT[w] | (A << 5) | A) if direction == :forward
657
- emit_word(CLZ[w] | (A << 5) | A)
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
- emit_add_imm(A, SP, 0, shift12: false) # mov A, sp (the block base)
690
- store_reg(A, dst)
691
- end
692
-
693
- # :div/:mod/:udiv/:umod. The quotient is `sdiv`/`udiv` of A by B; a
694
- # remainder is then A - quotient*B via `msub`, since AArch64 has no direct
695
- # remainder instruction. The signed/unsigned split mirrors the IR's own.
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
- load_reg(A, a)
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 | (B << 16) | (A << 10) | (C << 5) | A) # msub A, C, B, A
705
- store_reg(A, dst)
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
- store_reg(C, dst)
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 B's low bits (masked to 5 bits for
712
- # a 32-bit operand, 6 for a 64-bit one by the hardware, matching C).
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
- load_reg(A, a)
715
- load_reg(B, b)
716
- emit_word(base_table[width(size)] | (B << 16) | (A << 5) | A)
717
- store_reg(A, dst)
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
- load_reg(A, src)
723
- emit_word(SUB_SHIFTED[width(size)] | (A << 16) | (XZR << 5) | A)
724
- store_reg(A, dst)
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
- def emit_comparison(dst, a, b, condition, size)
732
- load_reg(A, a)
733
- load_reg(B, b)
734
- emit_word(SUBS_SHIFTED[width(size)] | (B << 16) | (A << 5) | XZR) # cmp A, B (subs xzr, A, B)
735
- emit_cset(A, condition)
736
- store_reg(A, dst)
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
- load_reg(A, src)
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 | (A << 5) | A)
751
- store_reg(A, dst)
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
- load_reg(A, src)
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 | (A << 5) | A)
766
- store_reg(A, dst)
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
- load_reg(A, ptr) # A = pointer value
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 | (A << 5) | A)
783
- store_reg(A, dst)
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
- load_reg(A, ptr) # A = destination address
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 | (A << 5) | B)
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
- load_reg(A, cond)
1078
+ rt = operand_register(cond, A)
808
1079
  @fixups << [@code.bytesize, label_id, :cbz]
809
- emit_word(0x34000000 | A) # cbz w{A}, <patched>
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
- load_reg(A, dest_vreg) # A = destination address
1028
- load_reg(B, src_vreg) # B = source address
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
- load_frame_at(reg, slot_offset(vreg))
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
- emit_fp_slot_access(reg, slot_offset(vreg), size, load: true)
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
- load_fp(FA, inst.a, size)
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
- load_fp(FA, a, size)
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 | A) # adrp A, <page of sym>
1421
- emit_add_imm(A, A, 0, shift12: false) # add A, A, #:lo12:sym
1422
- store_reg(A, dst)
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 | A) # adrp A, <page of sym's GOT slot>
1437
- emit_word(0xF9400000 | (A << 5) | A) # ldr A, [A, #:got_lo12:sym]
1438
- store_reg(A, dst)
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
- emit_slot_address(A, offset)
1445
- store_reg(A, dst)
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