Class: Rubycc::IR::CallConvention

Inherits:
Object
  • Object
show all
Defined in:
lib/rubycc/ir/call_convention.rb,
lib/rubycc/ir/call_convention.rb

Overview

The part of a target's calling convention the IR generator has to know about, so that where every argument lands is decided once — where the argument's C type is still in hand — rather than guessed at by a backend that only sees a tag.

Three things are target-specific and all three live here:

  • how many registers there are to hand out. System V AMD64 offers six integer registers (rdi, rsi, rdx, rcx, r8, r9) and AAPCS64 eight (x0..x7); both offer eight vector ones. Getting the counts from the target is what lets a seventh integer argument reach x6 on aarch64 instead of arriving tagged for the stack.

  • how an aggregate is cut up and where the pieces go (#aggregate_plan). The System V eightbyte classification and the AAPCS64 HFA / 16-byte / by-reference rules disagree in ways that are silent when guessed at: struct { float a, b; } is one SSE eightbyte in xmm0 under System V and two single-precision registers, s0 and s1, under AAPCS64.

  • how the register files are consumed as the argument list is walked (#placer). Both conventions place an argument as a unit, but they differ on what happens when one does not fit: System V leaves the registers it did not use available to a later argument, while AAPCS64 declares the file exhausted (6.4.2 stage C sets NGRN or NSRN to eight), so a trailing int after a spilled two-register struct is in a register on x86-64 and on the stack on aarch64.

hidden_result_kind is the kind of the implicit pointer to a caller-provided result buffer: an ordinary leading integer argument under System V, and its own mechanism (:indirect_result, the x8 register) under AAPCS64.

The va_list_* trio is the fourth target-specific thing the convention owns, and it belongs here for the same reason the others do: a va_list walks exactly the register/stack layout the convention laid a call's arguments out in, so the shape of the tag, the type the front end declares __builtin_va_list to name, and the flavour of the va_arg walk are all facets of the one ABI. va_list_abi is a plain tag (:system_v / :aapcs64) the generator switches its va_arg lowering on: the two walks read a different structure with the offsets running opposite ways, so they are kept as separate lowerings rather than one merged over a descriptor — and keeping the System V path untouched is what guarantees its emitted code does not shift by a byte.

Direct Known Subclasses

AAPCS64Convention, SystemVAMD64Convention

Constant Summary collapse

SYSTEM_V_AMD64 =
SystemVAMD64Convention.new
AAPCS64 =
AAPCS64Convention.new

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(gp_registers:, fp_registers:, va_list_tag:, va_list_type:, va_list_abi:, hidden_result_kind: :gp) ⇒ CallConvention

Returns a new instance of CallConvention.



97
98
99
100
101
102
103
104
105
106
# File 'lib/rubycc/ir/call_convention.rb', line 97

def initialize(gp_registers:, fp_registers:, va_list_tag:, va_list_type:, va_list_abi:,
               hidden_result_kind: :gp)
  @gp_registers = gp_registers
  @fp_registers = fp_registers
  @hidden_result_kind = hidden_result_kind
  @va_list_tag = va_list_tag
  @va_list_type = va_list_type
  @va_list_abi = va_list_abi
  freeze
end

Instance Attribute Details

#fp_registersObject (readonly)

Returns the value of attribute fp_registers.



94
95
96
# File 'lib/rubycc/ir/call_convention.rb', line 94

def fp_registers
  @fp_registers
end

#gp_registersObject (readonly)

Returns the value of attribute gp_registers.



94
95
96
# File 'lib/rubycc/ir/call_convention.rb', line 94

def gp_registers
  @gp_registers
end

#hidden_result_kindObject (readonly)

Returns the value of attribute hidden_result_kind.



94
95
96
# File 'lib/rubycc/ir/call_convention.rb', line 94

def hidden_result_kind
  @hidden_result_kind
end

#va_list_abiObject (readonly)

Returns the value of attribute va_list_abi.



94
95
96
# File 'lib/rubycc/ir/call_convention.rb', line 94

def va_list_abi
  @va_list_abi
end

#va_list_tagObject (readonly)

Returns the value of attribute va_list_tag.



94
95
96
# File 'lib/rubycc/ir/call_convention.rb', line 94

def va_list_tag
  @va_list_tag
end

#va_list_typeObject (readonly)

Returns the value of attribute va_list_type.



94
95
96
# File 'lib/rubycc/ir/call_convention.rb', line 94

def va_list_type
  @va_list_type
end

Class Method Details

.memory_pieces(size) ⇒ Object

The pieces a value of size bytes is cut into when it travels in the stack argument area. Both conventions round a stack argument up to a multiple of eight and align it to at least eight, so a spilled value is ceil(size/8) whole eightbytes whatever shape it would have taken in registers — an aarch64 HFA that runs out of vector registers is passed as packed eightbytes, not as one stack slot per member.



114
115
116
# File 'lib/rubycc/ir/call_convention.rb', line 114

def self.memory_pieces(size)
  Array.new((size + 7) / 8) { |i| AbiPiece.new(offset: 8 * i, size: 8, kind: :mem) }
end

Instance Method Details

#aggregate_plan(_type) ⇒ Object

The convention's plan for passing an aggregate of type by value.

Raises:

  • (NotImplementedError)


119
120
121
# File 'lib/rubycc/ir/call_convention.rb', line 119

def aggregate_plan(_type)
  raise NotImplementedError
end

#placerObject

A fresh running placement of one argument list (see the Placer classes).

Raises:

  • (NotImplementedError)


124
125
126
# File 'lib/rubycc/ir/call_convention.rb', line 124

def placer
  raise NotImplementedError
end