Class: Rubycc::IR::Generator
- Inherits:
-
Object
- Object
- Rubycc::IR::Generator
- Defined in:
- lib/rubycc/ir/generator.rb
Overview
Lowers the AST into IR. A straightforward post-order walk that allocates a fresh virtual register for every computed value, tracking each expression's static type so pointer operations can be type-checked and lowered. No optimization.
Defined Under Namespace
Classes: AddressConstant, Local, NotAddressConstant, ObjectRecord
Constant Summary collapse
- PAD_GP_PIECE =
The two alignment-pad ABI pieces (see AAPCS64::Placer#pad_gp/#pad_stack). A pad reserves one integer register (:pad) or one stack eightbyte (:pad_stack) ahead of a 16-byte-aligned aggregate so it starts on an aligned boundary; it moves no data, so its offset and size go unread — only its kind, which steers the backend's counter over it.
AbiPiece.new(offset: 0, size: 8, kind: :pad)
- PAD_STACK_PIECE =
AbiPiece.new(offset: 0, size: 8, kind: :pad_stack)
- STACK_OBJECT_ALIGNMENT =
The boundaries an automatic object is guaranteed to land on. Both backends build the frame from a 16-byte-aligned base and then place each stack object (an aggregate or a 128-bit integer) a 16-byte-rounded distance from it, while a scalar lives in one cell of the 8-byte virtual-register run. An _Alignas asking for more than that would need a prologue that realigns the stack pointer at run time, which neither backend emits, so #reject_overaligned_automatic refuses the declaration rather than letting it compile to a weaker boundary than it asked for.
16- VREG_SLOT_ALIGNMENT =
8
Instance Method Summary collapse
-
#generate(program, pic: false) ⇒ Object
Returns an IR::Program: an IR::Function per AST::FunctionDef plus the translation unit's read-only string pool.
-
#initialize(plain_char: Type::Char, convention: CallConvention::SYSTEM_V_AMD64) ⇒ Generator
constructor
plain_charis the Rubycc::Type of a plaincharon the target being generated for, matching what the parser resolved thecharspecifier to.
Constructor Details
#initialize(plain_char: Type::Char, convention: CallConvention::SYSTEM_V_AMD64) ⇒ Generator
plain_char is the Rubycc::Type of a plain char on the target being
generated for, matching what the parser resolved the char specifier to.
The generator needs it because a string literal's type is written here
rather than by the parser: its element type is plain char (6.4.5p6), so
a byte read out of one sign- or zero-extends following the target's
plain-char signedness. It defaults to the signed instance, the x86-64
System V choice, for a caller with no target in hand.
convention is the target's CallConvention, which fixes how many
registers an argument list may draw on before it spills to the stack.
It defaults to System V AMD64 for the same reason plain_char does.
89 90 91 92 |
# File 'lib/rubycc/ir/generator.rb', line 89 def initialize(plain_char: Type::Char, convention: CallConvention::SYSTEM_V_AMD64) @plain_char = plain_char @convention = convention end |
Instance Method Details
#generate(program, pic: false) ⇒ Object
Returns an IR::Program: an IR::Function per AST::FunctionDef plus the translation unit's read-only string pool. Prototypes (AST::FunctionDecl) contribute only a signature-table entry and emit no code. The table is filled in source order so a definition can reference itself (recursion) or an earlier prototype (mutual recursion), while a call to a still-unknown name is diagnosed as an implicit declaration.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/rubycc/ir/generator.rb', line 100 def generate(program, pic: false) # Position-independent code mode (-fPIC): when set, a reference that takes # the address of a file-scope object or function this translation unit # does not itself define is lowered through the Global Offset Table # (:got_addr) instead of a PC-relative :global_addr / :func_addr, so a # definition in another shared object can interpose on it. A symbol # defined here, a `static`, and a string literal keep the PC-relative # form. When false the lowering is byte-for-byte the non-PIC one. @pic = pic # name -> { param_types:, return_type:, variadic:, defined: }. # `param_types` is the array of parameter Rubycc::Types (its length being # the fixed arity — for a variadic function, only the named parameters); # `return_type` is the declared Rubycc::Type of a call to this function; # `variadic` is true for a "..."-terminated prototype (its calls admit # extra, promoted arguments past the fixed ones); `defined` distinguishes # a prototype from a completed definition so redefinitions can be # rejected. @signatures = {} # gcc provides memcpy as a builtin, and the parser rewrites # __builtin_memcpy(...) into a plain call to "memcpy". Seed its prototype # up front — void *memcpy(void *, const void *, unsigned long) — so such a # call compiles even when the translation unit never declares memcpy (no # <string.h>); a later, identical string.h prototype merges in without # conflict, and the reference resolves to libc's memcpy at link time. @signatures["memcpy"] = { param_types: [Type::Pointer.new(Type::Void), Type::Pointer.new(Type::Void), Type::ULong], return_type: Type::Pointer.new(Type::Void), variadic: false, defined: false } # The translation-unit-wide string pool: `@strings` holds each interned # byte string in id order, `@string_ids` maps content back to its id so # identical literals collapse to one entry (and one .rodata address). @strings = [] @string_ids = {} # File-scope variables: `@global_bindings` maps each name to its Local # binding (the outermost scope every function shares), while `@globals` # holds the IR::Global descriptors in source order for the compiler to # lay out into .data/.bss. @global_bindings = {} @globals = [] # File-scope objects that reserve storage, keyed by name: each ObjectRecord # tracks the merged state of a run of tentative/real definitions (6.9.2) — # its type, linkage, whether any declaration has initialized it, and the # index of its single IR::Global entry. A bare `extern` reference reserves # nothing and gets no record. A repeated declaration merges into the # record (types must agree); a second *initialized* definition is the real # redefinition error, and an object emitted tentatively in .bss is upgraded # in place to .data when a later declaration supplies an initializer. @object_records = {} # A monotonic counter that names each block-scope `static` uniquely as # "<var>.<n>". A '.' cannot appear in a C identifier, so these names # never collide with a real symbol; the counter runs over the whole # translation unit in source order, keeping the output deterministic (N4). @static_local_count = 0 ir_functions = [] # Declarations are processed in source order, so a function may only # reference a global or callee already declared above it (C's # declaration-before-use rule), and a name reused across the global and # function namespaces is rejected as a redefinition. program.functions.each do |decl| case decl when Front::AST::GlobalDecl declare_global(decl) when Front::AST::FunctionDecl # A prototype's storage class (`static`/`extern`) is recorded on the # AST but drives no behavior here: a declaration reserves nothing and # M1 does not diagnose a static/extern mismatch against the eventual # definition, so a prototype only contributes a signature. declare_function(decl.name, decl.return_type, decl.params.map(&:abi_type), variadic: decl.variadic, defined: false, token: decl.token) when Front::AST::FunctionDef # A signature is what *callers* must agree with, so it is built from # the types the parameters are passed as. The two differ only for an # old-style definition, whose narrow parameters arrive promoted # (see AST::Parameter#abi_type). declare_function(decl.name, decl.return_type, decl.params.map(&:abi_type), variadic: decl.variadic, defined: true, token: decl.token) # `static` gives the definition internal linkage (an STB_LOCAL text # symbol); an absent or `extern` specifier leaves it external. linkage = decl.storage == :static ? :internal : :external ir_functions << gen_function(decl, linkage) end end Program.new(ir_functions, @strings, @globals, array_entries(program, ir_functions), program.visibility_attributes) end |