Class: MilkTea::CBackend

Inherits:
Object
  • Object
show all
Includes:
AggregateUtils, ControlFlowEmission, Expressions, FeatureDetection, FormatHelpers, Reachability, Reinterpret, RuntimeHelpers, Statements, TypeCollectors, TypeDeclaration, TypeSystem
Defined in:
lib/milk_tea/core/c_backend.rb,
lib/milk_tea/core/c_backend/statements.rb,
lib/milk_tea/core/c_backend/expressions.rb,
lib/milk_tea/core/c_backend/reinterpret.rb,
lib/milk_tea/core/c_backend/type_system.rb,
lib/milk_tea/core/c_backend/reachability.rb,
lib/milk_tea/core/c_backend/format_helpers.rb,
lib/milk_tea/core/c_backend/aggregate_utils.rb,
lib/milk_tea/core/c_backend/runtime_helpers.rb,
lib/milk_tea/core/c_backend/type_collectors.rb,
lib/milk_tea/core/c_backend/type_declaration.rb,
lib/milk_tea/core/c_backend/feature_detection.rb,
lib/milk_tea/core/c_backend/control_flow_emission.rb

Defined Under Namespace

Modules: AggregateUtils, ControlFlowEmission, Expressions, FeatureDetection, FormatHelpers, Reachability, Reinterpret, RuntimeHelpers, Statements, TypeCollectors, TypeDeclaration, TypeSystem

Constant Summary collapse

INDENT =
"  "
ARRAY_OUT_PARAM_NAME =
"__mt_out"
LOOP_GUARD_MAX_ITERATIONS =

Max iterations for loop guard counters before aborting. Set high enough that legitimate data-bound loops (e.g. is_valid_utf8 scanning a 600 KB source file) never trigger a false positive, yet low enough that a genuinely infinite loop fires a clear fatal within ~1 s.

50_000_000
EQUALITY_OPERATORS =
["==", "!="].freeze

Constants included from TypeDeclaration

TypeDeclaration::C_KEYWORDS

Constants included from ControlFlowEmission

ControlFlowEmission::INVERTED_BOOLEAN_OPS

Constants included from FeatureDetection

FeatureDetection::FORMAT_HELPER_DEPENDENCIES

Constants included from TypeSystem

TypeSystem::PRIMITIVE_C_TYPE_MAP, TypeSystem::SANITIZE_LEADING_UNDERSCORES_RE, TypeSystem::SANITIZE_NON_ALNUM_RE, TypeSystem::SANITIZE_TRAILING_UNDERSCORES_RE, TypeSystem::SANITIZE_UNDERSCORES_RE

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Reinterpret

#c_operator, #checked_array_index_helper_name, #checked_span_index_helper_name, #collect_reinterpret_helpers, #collect_reinterpret_helpers_from_expression, #collect_reinterpret_helpers_from_statements, #emit_reinterpret_helper, #function_or_proc_element_type?, #identity_cast?, #identity_reinterpret?, #nullable_array_index_helper_name, #nullable_pointer_like_type?, #nullable_span_index_helper_name, #pointer_member_receiver?, #reinterpret_helper_name, #variant_arm_payload_field_type, #void_type, #wrap_index_receiver, #wrap_member_receiver, #wrap_pointer_member_receiver

Methods included from Expressions

#aggregate_field_type, #binary_precedence, #c_expression_lvalue?, #call_emission_params, #emit_address_of_operand, #emit_addressof_field_initializer, #emit_aggregate_field_initializer, #emit_aggregate_initializer, #emit_aggregate_literal, #emit_array_call_statement, #emit_array_compound_literal, #emit_array_copy_statement, #emit_array_initializer, #emit_array_out_argument, #emit_array_return, #emit_binary_expression, #emit_binary_operand, #emit_call_callee, #emit_call_expression, #emit_cast_operand, #emit_conditional_condition, #emit_conditional_expression, #emit_cyclic_array_initializer, #emit_cyclic_struct_initializer, #emit_expression, #emit_float_literal, #emit_initializer, #emit_nullable_null_comparison, #emit_nullable_some_initializer, #emit_simd_lane_with, #emit_str_initializer, #emit_str_literal, #emit_string_equality_expression, #emit_struct_equality_expression, #emit_variant_equality_expression, #emit_variant_field_initializer, #emit_variant_initializer, #emit_variant_literal, #emit_void_expression, #emit_void_field_initializer, #emit_zero_expression, #emit_zero_initializer, #layout_type_expression, #nullable_null_comparison?, #nullable_value_type?, #omitted_method_receiver_call?, #omitted_method_receiver_function?, #omitted_method_receiver_function_names, #string_equality_expression?, #struct_equality_expression?, #struct_equality_helper_name, #struct_equality_type?, #variant_equality_expression?, #variant_equality_helper_name, #void_typed_field?, #wrap_expression

Methods included from TypeDeclaration

#emit_enum, #emit_forward_declarations, #emit_function, #emit_function_declarations, #emit_simd_type, #emit_soa_type, #emit_span_type, #emit_string_type, #emit_struct, #emit_union, #emit_variant, #emit_vector_math_typedefs, #function_params, #function_signature, #sanitize_c_identifier, #struct_layout_attributes

Methods included from FormatHelpers

#emit_fmt_builder_helpers, #emit_format_helpers

Methods included from RuntimeHelpers

#emit_async_memory_helpers, #emit_checked_array_index_helper, #emit_checked_span_index_helper, #emit_detach_helpers, #emit_entrypoint_argv_helpers, #emit_fatal_helper, #emit_field_equality_guards, #emit_foreign_temp_cstr_helpers, #emit_nullable_array_index_helper, #emit_nullable_span_index_helper, #emit_parallel_for_helper, #emit_spawn_all_helper, #emit_str_buffer_helpers, #emit_str_concat_helper, #emit_str_equality_helper, #emit_str_literal_constants, #emit_struct_equality_helper, #emit_struct_equality_helpers, #emit_utf8_validation_helpers, #emit_variant_equality_helper, #emit_variant_equality_helpers, #str_literal_name

Methods included from ControlFlowEmission

#block_requires_break_label?, #body_has_sequential_fallthrough?, #body_needs_fallback_return?, #body_terminates?, #can_emit_switch_as_if?, #canonicalize_top_guarded_while, #collect_used_labels, #collect_used_labels_from_statements, #constant_boolean_value, #constant_integer_value, #contains_visible_loop_exit?, #emit_if_statement, #emit_switch_as_if_statement, #extract_nested_else_if, #invert_break_guard_condition, #inverted_boolean_operator, #loop_break_label_name, #loop_continue_label_name, #statement_prevents_c_fallthrough?, #statement_prevents_sequential_fallthrough?, #statement_references_label?, #statement_requires_break_label?, #statement_terminates?, #statements_reference_label?, #strip_terminal_switch_break, #switch_case_body_prevents_outer_fallthrough?, #switch_case_statement_prevents_outer_fallthrough?, #switch_statement_prevents_outer_fallthrough?, #top_guard_break_condition

Methods included from Statements

#bool_type?, #checked_index_alias, #collect_checked_index_alias_candidates, #collect_checked_index_aliases, #collect_checked_index_aliases_for_statement, #compiler_generated_local_name?, #emit_assignment_expression, #emit_checked_index_alias_declarations, #emit_checked_index_pointer, #emit_for_loop_clause, #emit_statement, #emit_statement_sequence, #emit_static_assert, #fold_nested_redundant_statement_temps, #fold_redundant_statement_temps, #fold_single_use_bool_if_temp, #fold_single_use_local_alias, #fresh_checked_index_alias_name, #hoistable_checked_index_alias?, #name_reference_count_in_expression, #name_reference_count_in_statement, #name_reference_count_in_statements, #side_effect_free_expression?, #single_use_bool_if_condition_kind, #statements_require_scope?, #with_checked_index_aliases

Methods included from TypeCollectors

#collect_checked_array_index_types, #collect_checked_array_index_types_from_expression, #collect_checked_array_index_types_from_statements, #collect_checked_span_index_types, #collect_checked_span_index_types_from_expression, #collect_checked_span_index_types_from_statements, #collect_dyn_decls, #collect_dyn_types, #collect_generic_struct_decls, #collect_generic_struct_type, #collect_generic_struct_types, #collect_generic_struct_types_from_expression, #collect_generic_struct_types_from_statements, #collect_generic_variant_decls, #collect_generic_variant_type, #collect_generic_variant_types, #collect_generic_variant_types_from_expression, #collect_generic_variant_types_from_statements, #collect_nullable_opt_decls, #collect_nullable_opt_types, #collect_proc_decls, #collect_proc_type, #collect_proc_types, #collect_proc_types_from_expression, #collect_proc_types_from_statements, #collect_simd_from_statements, #collect_simd_type, #collect_simd_type_in_expression, #collect_simd_types, #collect_soa_from_statements, #collect_soa_type, #collect_soa_type_in_expression, #collect_soa_types, #collect_span_type, #collect_span_types, #collect_span_types_from_expression, #collect_span_types_from_statements, #collect_str_buffer_decls, #collect_str_buffer_type, #collect_str_buffer_types, #collect_str_buffer_types_from_expression, #collect_str_buffer_types_from_statements, #collect_str_literals, #collect_str_literals_from_expression, #collect_str_literals_from_statements, #collect_task_decls, #collect_task_type, #collect_task_types, #collect_task_types_from_expression, #collect_task_types_from_statements, #each_ir_statement_for_type_collection, #proc_field_types

Methods included from FeatureDetection

#aggregate_contains_string_view?, #aggregate_equality_needs_string_view?, #aggregate_equality_type?, #aggregate_equality_types, #collect_aggregate_equality_dependencies, #collect_aggregate_equality_from_expression, #collect_value_field_equality_dependencies, #expression_uses_named_call?, #expression_uses_str_equality?, #expression_uses_vector_math?, #format_helper_callees, #function_uses_named_call?, #function_uses_str_equality?, #required_format_helper_callees, #statement_matches_expression_predicate?, #statement_uses_named_call?, #statement_uses_str_equality?, #statement_uses_vector_math?, #struct_equality_types, #type_contains_string_view?, #type_contains_vector_math?, #uses_async_memory_helpers?, #uses_cyclic_variant_heap_copy?, #uses_detach_helper?, #uses_entrypoint_argv_helpers?, #uses_fatal_helper?, #uses_fmt_builder?, #uses_foreign_temp_cstr_helpers?, #uses_format_helpers?, #uses_mt_fatal_helper?, #uses_mt_fatal_str_helper?, #uses_parallel_for_helper?, #uses_spawn_all_helper?, #uses_str_buffer_helpers?, #uses_str_concat_helper?, #uses_str_equality_helper?, #uses_string_view?, #uses_struct_equality_helper?, #uses_variant_equality_helper?, #uses_vector_math_types?, #variant_equality_types

Methods included from Reachability

#add_expr_type_module, #add_statement_type_modules, #add_type_module, #all_emitted_top_level_values, #collect_active_module_names, #collect_called_function_names_from_expression, #collect_called_function_names_from_statements, #collect_referenced_constant_names_from_expression, #collect_referenced_constant_names_from_statements, #collect_type_referenced_module_names, #constant_reference_visitor, #emitted_aggregate_structs, #emitted_aggregate_unions, #emitted_aggregate_variants, #emitted_constants, #emitted_functions, #emitted_globals, #filter_by_type_reachability, #traverse_ir_expression, #traverse_ir_statements

Methods included from TypeSystem

#array_element_type, #array_length, #array_out_param_declaration, #array_type?, #c_backend_pointer_like_type?, #c_declaration, #c_declaration_parts, #c_field_declaration, #c_function_declaration, #c_function_return_type, #c_type, #const_pointer_type?, #constant_storage, #declarator_needs_grouping?, #dyn_type_name, #external_opaque_c_type, #generic_c_type, #global_storage, #module_c_prefix, #mutable_pointer_type?, #named_type_c_name, #nullable_opt_type_name, #own_type?, #pointer_to, #pointer_type?, #primitive_c_type, #proc_type_name, #raw_pointer_type?, #ref_type?, #sanitize_identifier, #simd_type_name, #soa_type_name, #span_type_name, #str_buffer_capacity, #str_buffer_storage_capacity, #str_buffer_type?, #str_buffer_type_name, #task_type_name, #tuple_type_name

Methods included from AggregateUtils

#aggregate_can_reach?, #aggregate_decl_dependencies, #aggregate_field_creates_cycle?, #aggregate_type_dependencies, #build_cyclic_aggregate_pairs, #each_variant_arm_field_type, #resolved_aggregate_c_name, #sort_aggregate_decls

Constructor Details

#initialize(program, emit_line_directives: true, debug_guards: false) ⇒ CBackend

Returns a new instance of CBackend.



76
77
78
79
80
81
82
83
84
85
86
# File 'lib/milk_tea/core/c_backend.rb', line 76

def initialize(program, emit_line_directives: true, debug_guards: false)
  @program = program
  @emit_line_directives = emit_line_directives
  @debug_guards = debug_guards
  @checked_index_alias_stack = []
  @checked_index_alias_id = 0
  @loop_guard_id = 0
  @emitted_span_type_names = Set.new
  @source_path = program.path
  @current_line = nil
end

Class Method Details

.emit(program, emit_line_directives: true, debug_guards: false) ⇒ Object



68
69
70
# File 'lib/milk_tea/core/c_backend.rb', line 68

def self.emit(program, emit_line_directives: true, debug_guards: false)
  new(program, emit_line_directives:, debug_guards:).emit
end

.generate_c(ir_program, emit_line_directives: true, debug_guards: false) ⇒ Object



72
73
74
# File 'lib/milk_tea/core/c_backend.rb', line 72

def self.generate_c(ir_program, emit_line_directives: true, debug_guards: false)
  emit(ir_program, emit_line_directives:, debug_guards:)
end

Instance Method Details

#emitObject



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/milk_tea/core/c_backend.rb', line 88

def emit
  lines = []
  @cyclic_aggregate_pairs = build_cyclic_aggregate_pairs(
    emitted_aggregate_structs + collect_generic_struct_decls + collect_task_decls + collect_proc_decls + collect_dyn_decls + collect_str_buffer_decls + collect_nullable_opt_decls +
    emitted_aggregate_unions +
    emitted_aggregate_variants + collect_generic_variant_decls
  )

  constants = emitted_constants

  emit_preamble(lines)
  emit_conditional_helpers(lines)
  emit_type_declarations(lines)
  emit_function_forward_declarations(lines)
  emit_globals_and_constants(lines, constants)
  emit_per_type_helpers(lines)
  emit_string_literal_constants_section(lines)
  emit_function_definitions(lines)

  lines.join("\n").rstrip + "\n"
end

#emit_aggregate_equality_forward_declarationsObject



271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/milk_tea/core/c_backend.rb', line 271

def emit_aggregate_equality_forward_declarations
  lines = []
  struct_equality_types.each do |type|
    c = named_type_c_name(type)
    lines << "static bool mt_struct_eq_#{c}(struct #{c} left, struct #{c} right);"
  end
  variant_equality_types.each do |type|
    c = named_type_c_name(type)
    lines << "static bool mt_variant_eq_#{c}(struct #{c} left, struct #{c} right);"
  end
  lines
end

#emit_conditional_helpers(lines) ⇒ Object



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
# File 'lib/milk_tea/core/c_backend.rb', line 136

def emit_conditional_helpers(lines)
  if uses_string_view?
    lines.concat(emit_string_type)
    lines << ""
  end
  if uses_vector_math_types?
    lines.concat(emit_vector_math_typedefs)
    lines << ""
  end
  if uses_fatal_helper?
    lines.concat(emit_fatal_helper)
    lines << ""
  end
  if uses_format_helpers?
    lines.concat(emit_format_helpers)
    lines << ""
  end
  if uses_fmt_builder?
    lines.concat(emit_fmt_builder_helpers)
    lines << ""
  end
  if uses_str_equality_helper?
    lines.concat(emit_str_equality_helper)
    lines << ""
  end
  if uses_str_concat_helper?
    lines.concat(emit_str_concat_helper)
    lines << ""
  end
  if uses_str_buffer_helpers?
    lines.concat(emit_utf8_validation_helpers)
    lines << ""
  end
  if uses_async_memory_helpers?
    lines.concat(emit_async_memory_helpers)
    lines << ""
  end
  if uses_parallel_for_helper?
    lines.concat(emit_parallel_for_helper)
    lines << ""
  end
  if uses_spawn_all_helper?
    lines.concat(emit_spawn_all_helper)
    lines << ""
  end
  if uses_detach_helper?
    lines.concat(emit_detach_helpers)
    lines << ""
  end
end

#emit_function_definitions(lines) ⇒ Object



359
360
361
362
363
364
# File 'lib/milk_tea/core/c_backend.rb', line 359

def emit_function_definitions(lines)
  emitted_functions.each do |function|
    lines.concat(emit_function(function))
    lines << ""
  end
end

#emit_function_forward_declarations(lines) ⇒ Object



292
293
294
295
296
297
298
# File 'lib/milk_tea/core/c_backend.rb', line 292

def emit_function_forward_declarations(lines)
  function_declarations = emit_function_declarations(emitted_functions)
  unless function_declarations.empty?
    lines.concat(function_declarations)
    lines << ""
  end
end

#emit_globals_and_constants(lines, constants) ⇒ Object



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/milk_tea/core/c_backend.rb', line 300

def emit_globals_and_constants(lines, constants)
  constants.each do |constant|
    @current_line = constant.line if constant.line
    @source_path ||= constant.path if constant.path
    lines << "#{constant_storage(constant.type)} #{c_declaration(constant.type, constant.linkage_name)} = #{emit_initializer(constant.value)};"
  end
  @program.globals.each do |global|
    next unless emitted_globals.include?(global)
    if global.respond_to?(:line) && global.line
      @current_line = global.line
      @source_path ||= global.path if global.respond_to?(:path) && global.path
    end
    lines << "#{global_storage(global.type)} #{c_declaration(global.type, global.linkage_name)} = #{emit_initializer(global.value)};"
  end
  lines << "" unless constants.empty? && @program.globals.empty?

  @program.static_asserts.each do |statement|
    lines << emit_static_assert(statement)
  end
  lines << "" unless @program.static_asserts.empty?
end

#emit_per_type_helpers(lines) ⇒ Object



322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
# File 'lib/milk_tea/core/c_backend.rb', line 322

def emit_per_type_helpers(lines)
  collect_reinterpret_helpers.each do |helper|
    lines.concat(emit_reinterpret_helper(helper))
    lines << ""
  end

  collect_checked_array_index_types.each do |type|
    lines.concat(emit_checked_array_index_helper(type))
    lines << ""
  end

  collect_checked_span_index_types.each do |type|
    lines.concat(emit_checked_span_index_helper(type))
    lines << ""
  end

  collect_checked_array_index_types(nullable_only: true).each do |type|
    lines.concat(emit_nullable_array_index_helper(type))
    lines << ""
  end

  collect_checked_span_index_types(nullable_only: true).each do |type|
    lines.concat(emit_nullable_span_index_helper(type))
    lines << ""
  end
end

#emit_preamble(lines) ⇒ Object



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
# File 'lib/milk_tea/core/c_backend.rb', line 110

def emit_preamble(lines)
  headers = @program.includes.map(&:header)
  if headers.include?("\"fs_support.h\"") || headers.include?("\"tls_support.h\"") || uses_parallel_for_helper? || uses_spawn_all_helper? || uses_detach_helper?
    lines << "#ifndef _GNU_SOURCE"
    lines << "#define _GNU_SOURCE"
    lines << "#endif"
    lines << "#ifndef _POSIX_C_SOURCE"
    lines << "#define _POSIX_C_SOURCE 200809L"
    lines << "#endif"
    lines << ""
  end
  if uses_fatal_helper? || uses_format_helpers?
    headers << "<stdio.h>"
  end
  if uses_fatal_helper? || uses_format_helpers? || uses_async_memory_helpers? || uses_foreign_temp_cstr_helpers? || uses_detach_helper? || uses_cyclic_variant_heap_copy?
    headers << "<stdlib.h>"
  end
  if uses_parallel_for_helper? || uses_spawn_all_helper? || uses_detach_helper?
    headers << "\"uv.h\""
  end
  headers.uniq.each do |header|
    lines << "#include #{header}"
  end
  lines << ""
end

#emit_str_equality_support(lines) ⇒ Object



284
285
286
287
288
289
290
# File 'lib/milk_tea/core/c_backend.rb', line 284

def emit_str_equality_support(lines)
  return if @emitted_aggregate_str_equality_support

  @emitted_aggregate_str_equality_support = true
  lines.concat(emit_string_type) unless uses_string_view?
  lines.concat(emit_str_equality_helper)
end

#emit_string_literal_constants_section(lines) ⇒ Object



349
350
351
352
353
354
355
356
357
# File 'lib/milk_tea/core/c_backend.rb', line 349

def emit_string_literal_constants_section(lines)
  str_literals = collect_str_literals
  return if str_literals.empty?

  @str_literal_map = {}
  str_literals.each_with_index { |value, i| @str_literal_map[value] = str_literal_name(i) }
  lines.concat(emit_str_literal_constants(str_literals))
  lines << ""
end

#emit_type_declarations(lines) ⇒ Object



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/milk_tea/core/c_backend.rb', line 187

def emit_type_declarations(lines)
  all_struct_decls = emitted_aggregate_structs + collect_generic_struct_decls + collect_task_decls + collect_proc_decls + collect_dyn_decls + collect_str_buffer_decls + collect_nullable_opt_decls
  all_variant_decls = emitted_aggregate_variants + collect_generic_variant_decls
  aggregate_decls = sort_aggregate_decls(all_struct_decls, emitted_aggregate_unions, all_variant_decls)

  forward_declarations = emit_forward_declarations(@program.opaques, aggregate_decls)
  unless forward_declarations.empty?
    lines.concat(forward_declarations)
    lines << ""
  end

  @program.enums.each do |enum_decl|
    lines.concat(emit_enum(enum_decl))
    lines << ""
  end

  collect_span_types.each do |type|
    span_name = span_type_name(type)
    next if @emitted_span_type_names.include?(span_name)
    @emitted_span_type_names.add(span_name)
    lines.concat(emit_span_type(type))
    lines << ""
  end

  collect_soa_types.each do |type|
    lines.concat(emit_soa_type(type))
    lines << ""
  end

  collect_simd_types.each do |type|
    next if @emitted_simd_type_names&.include?(simd_type_name(type))
    @emitted_simd_type_names ||= Set.new
    @emitted_simd_type_names.add(simd_type_name(type))
    lines.concat(emit_simd_type(type))
    lines << ""
  end

  if uses_entrypoint_argv_helpers?
    lines.concat(emit_entrypoint_argv_helpers)
    lines << ""
  end

  if uses_foreign_temp_cstr_helpers?
    lines.concat(emit_foreign_temp_cstr_helpers)
    lines << ""
  end

  if uses_str_buffer_helpers?
    lines.concat(emit_str_buffer_helpers)
    lines << ""
  end

  aggregate_decls.each do |aggregate_decl|
    case aggregate_decl
    when IR::StructDecl
      lines.concat(emit_struct(aggregate_decl))
    when IR::UnionDecl
      lines.concat(emit_union(aggregate_decl))
    when IR::VariantDecl
      lines.concat(emit_variant(aggregate_decl))
    end
    lines << ""
  end

  if uses_variant_equality_helper? || uses_struct_equality_helper?
    lines.concat(emit_aggregate_equality_forward_declarations)
    lines << ""
  end
  if uses_variant_equality_helper?
    unless uses_str_equality_helper?
      emit_str_equality_support(lines) if aggregate_equality_needs_string_view?
    end
    lines.concat(emit_variant_equality_helpers)
    lines << ""
  end
  if uses_struct_equality_helper?
    unless uses_str_equality_helper?
      emit_str_equality_support(lines) if aggregate_equality_needs_string_view?
    end
    lines.concat(emit_struct_equality_helpers)
    lines << ""
  end
end