Module: MilkTea::CBackend::Statements
- Included in:
- MilkTea::CBackend
- Defined in:
- lib/milk_tea/core/c_backend/statements.rb
Instance Method Summary collapse
- #block_requires_scope?(statements) ⇒ Boolean
- #bool_type?(type) ⇒ Boolean
- #checked_index_alias(expression) ⇒ Object
- #checked_index_aliases_for_statement(statement) ⇒ Object
- #collect_checked_index_alias_candidates(expression, counts, order) ⇒ Object
- #collect_checked_index_aliases(expressions) ⇒ Object
- #compact_generated_statement_sequence(statements) ⇒ Object
- #compiler_generated_local_name?(name) ⇒ Boolean
- #emit_assignment_expression(assignment, indent) ⇒ Object
- #emit_checked_index_alias_declarations(aliases, indent) ⇒ Object
- #emit_checked_index_pointer(expression) ⇒ Object
- #emit_for_clause_statement(statement) ⇒ Object
- #emit_statement(statement, level, function:, used_labels:, loop_continue_label: nil, loop_break_label: nil, remaining_statements: []) ⇒ Object
- #emit_statement_sequence(statements, level, function:, used_labels:, loop_continue_label: nil, loop_break_label: nil) ⇒ Object
- #emit_static_assert(statement) ⇒ Object
- #fold_single_use_bool_if_temp(local_decl, if_stmt, remaining_statements) ⇒ Object
- #fold_single_use_local_alias(source_decl, alias_decl, remaining_statements) ⇒ Object
- #fresh_checked_index_alias_name ⇒ Object
- #hoistable_checked_index_alias?(expression) ⇒ Boolean
- #name_reference_count_in_expression(expression, name) ⇒ Object
- #name_reference_count_in_statement(statement, name) ⇒ Object
- #name_reference_count_in_statements(statements, name) ⇒ Object
- #side_effect_free_expression?(expression) ⇒ Boolean
- #single_use_bool_if_condition_kind(condition, temp_name) ⇒ Object
- #transform_compactable_nested_bodies(statement) ⇒ Object
- #with_checked_index_aliases(aliases) ⇒ Object
Instance Method Details
#block_requires_scope?(statements) ⇒ Boolean
527 528 529 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 527 def block_requires_scope?(statements) statements.any? { |statement| statement.is_a?(IR::LocalDecl) } end |
#bool_type?(type) ⇒ Boolean
383 384 385 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 383 def bool_type?(type) type.is_a?(Types::Primitive) && type.name == "bool" end |
#checked_index_alias(expression) ⇒ Object
518 519 520 521 522 523 524 525 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 518 def checked_index_alias(expression) @checked_index_alias_stack.reverse_each do |aliases| alias_name = aliases[expression] return alias_name if alias_name end nil end |
#checked_index_aliases_for_statement(statement) ⇒ Object
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 387 def checked_index_aliases_for_statement(statement) expressions = case statement when IR::LocalDecl [statement.value] when IR::Assignment [statement.target, statement.value] when IR::ExpressionStmt [statement.expression] when IR::ReturnStmt statement.value ? [statement.value] : [] else [] end collect_checked_index_aliases(expressions.compact) end |
#collect_checked_index_alias_candidates(expression, counts, order) ⇒ Object
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 419 def collect_checked_index_alias_candidates(expression, counts, order) case expression when IR::Member collect_checked_index_alias_candidates(expression.receiver, counts, order) when IR::Index collect_checked_index_alias_candidates(expression.receiver, counts, order) collect_checked_index_alias_candidates(expression.index, counts, order) when IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex order << expression unless counts.key?(expression) counts[expression] += 1 collect_checked_index_alias_candidates(expression.receiver, counts, order) collect_checked_index_alias_candidates(expression.index, counts, order) when IR::Call collect_checked_index_alias_candidates(expression.callee, counts, order) unless expression.callee.is_a?(String) expression.arguments.each { |argument| collect_checked_index_alias_candidates(argument, counts, order) } when IR::Unary collect_checked_index_alias_candidates(expression.operand, counts, order) when IR::Binary collect_checked_index_alias_candidates(expression.left, counts, order) collect_checked_index_alias_candidates(expression.right, counts, order) when IR::Conditional collect_checked_index_alias_candidates(expression.condition, counts, order) collect_checked_index_alias_candidates(expression.then_expression, counts, order) collect_checked_index_alias_candidates(expression.else_expression, counts, order) when IR::ReinterpretExpr, IR::AddressOf, IR::Cast collect_checked_index_alias_candidates(expression.expression, counts, order) when IR::AggregateLiteral expression.fields.each { |field| collect_checked_index_alias_candidates(field.value, counts, order) } when IR::ArrayLiteral expression.elements.each { |element| collect_checked_index_alias_candidates(element, counts, order) } when IR::VariantLiteral expression.fields.each { |field| collect_checked_index_alias_candidates(field.value, counts, order) } end end |
#collect_checked_index_aliases(expressions) ⇒ Object
404 405 406 407 408 409 410 411 412 413 414 415 416 417 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 404 def collect_checked_index_aliases(expressions) counts = Hash.new(0) order = [] expressions.each do |expression| collect_checked_index_alias_candidates(expression, counts, order) end order.each_with_object({}) do |expression, aliases| next unless counts[expression] > 1 next unless hoistable_checked_index_alias?(expression) aliases[expression] = fresh_checked_index_alias_name end end |
#compact_generated_statement_sequence(statements) ⇒ Object
186 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 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 186 def compact_generated_statement_sequence(statements) transformed = statements.map { |statement| transform_compactable_nested_bodies(statement) } compacted = [] index = 0 reachable = true while index < transformed.length current = transformed[index] unless reachable if current.is_a?(IR::LabelStmt) compacted << current reachable = true end index += 1 next end following = transformed[index + 1] remaining = transformed[(index + 2)..] || [] if following && (folded_local_alias = fold_single_use_local_alias(current, following, remaining)) compacted << folded_local_alias reachable = !statement_prevents_sequential_fallthrough?(folded_local_alias) index += 2 next end if following && (folded_if = fold_single_use_bool_if_temp(current, following, remaining)) compacted << folded_if reachable = !statement_prevents_sequential_fallthrough?(folded_if) index += 2 next end compacted << current reachable = !statement_prevents_sequential_fallthrough?(current) index += 1 end compacted end |
#compiler_generated_local_name?(name) ⇒ Boolean
279 280 281 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 279 def compiler_generated_local_name?(name) name.start_with?("__mt_") end |
#emit_assignment_expression(assignment, indent) ⇒ Object
560 561 562 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 560 def emit_assignment_expression(assignment, indent) ["#{indent}#{emit_expression(assignment.target)} #{assignment.operator} #{emit_expression(assignment.value)};"] end |
#emit_checked_index_alias_declarations(aliases, indent) ⇒ Object
489 490 491 492 493 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 489 def emit_checked_index_alias_declarations(aliases, indent) aliases.map do |expression, alias_name| "#{indent}#{c_declaration(pointer_to(expression.type), alias_name)} = #{emit_checked_index_pointer(expression)};" end end |
#emit_checked_index_pointer(expression) ⇒ Object
495 496 497 498 499 500 501 502 503 504 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 495 def emit_checked_index_pointer(expression) case expression when IR::CheckedIndex "#{checked_array_index_helper_name(expression.receiver_type)}(#{emit_address_of_operand(expression.receiver)}, #{emit_expression(expression.index)})" when IR::CheckedSpanIndex "#{checked_span_index_helper_name(expression.receiver_type)}(#{emit_expression(expression.receiver)}, #{emit_expression(expression.index)})" else raise CBackendError.new("unsupported checked index alias expression #{expression.class.name}", line: 0, column: 0, path: @path) end end |
#emit_for_clause_statement(statement) ⇒ Object
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 531 def emit_for_clause_statement(statement) case statement when IR::LocalDecl raise CBackendError.new("array for-loop init declarations are unsupported", line: 0, column: 0, path: @path) if array_type?(statement.type) "#{c_declaration(statement.type, statement.linkage_name)} = #{emit_initializer(statement.value)}" when IR::Assignment if array_type?(statement.target.type) && statement.operator == "=" raise CBackendError.new("array for-loop assignment clauses are unsupported", line: 0, column: 0, path: @path) end "#{emit_expression(statement.target)} #{statement.operator} #{emit_expression(statement.value)}" when IR::ExpressionStmt emit_expression(statement.expression) else raise CBackendError.new("unsupported for-loop clause #{statement.class.name}", line: 0, column: 0, path: @path) end end |
#emit_statement(statement, level, function:, used_labels:, loop_continue_label: nil, loop_break_label: nil, remaining_statements: []) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 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 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 20 def emit_statement(statement, level, function:, used_labels:, loop_continue_label: nil, loop_break_label: nil, remaining_statements: []) indent = INDENT * level aliases = checked_index_aliases_for_statement(statement) alias_lines = emit_checked_index_alias_declarations(aliases, indent) if statement.respond_to?(:line) && statement.line @current_line = statement.line @source_path ||= statement.path if statement.respond_to?(:path) && statement.path end line_directive = if @emit_line_directives && statement.respond_to?(:line) && statement.line sp = (statement.respond_to?(:path) && statement.path) || @source_path sp ? ["#line #{statement.line} #{sp.inspect}"] : [] else [] end statement_lines = with_checked_index_aliases(aliases) do case statement when IR::LocalDecl if array_type?(statement.type) && statement.value.is_a?(IR::Call) lines = ["#{indent}#{c_declaration(statement.type, statement.linkage_name)};"] lines << emit_array_call_statement(statement.value, emit_array_out_argument(statement.linkage_name), indent) lines elsif array_type?(statement.type) && !statement.value.is_a?(IR::ArrayLiteral) && !statement.value.is_a?(IR::ZeroInit) lines = ["#{indent}#{c_declaration(statement.type, statement.linkage_name)};"] lines << emit_array_copy_statement(statement.linkage_name, statement.value, indent) lines else [ "#{indent}#{c_declaration(statement.type, statement.linkage_name)} = #{emit_initializer(statement.value)};", ] end when IR::Assignment if array_type?(statement.target.type) && statement.operator == "=" && statement.value.is_a?(IR::Call) [emit_array_call_statement(statement.value, emit_array_out_argument(emit_expression(statement.target)), indent)] elsif array_type?(statement.target.type) && statement.operator == "=" [emit_array_copy_statement(emit_expression(statement.target), statement.value, indent)] else ["#{indent}#{emit_expression(statement.target)} #{statement.operator} #{emit_expression(statement.value)};"] end when IR::BlockStmt if block_requires_scope?(statement.body) lines = ["#{indent}{"] lines.concat(emit_statement_sequence(statement.body, level + 1, function:, used_labels:, loop_continue_label:, loop_break_label:)) lines << "#{indent}}" lines else emit_statement_sequence(statement.body, level, function:, used_labels:, loop_continue_label:, loop_break_label:) end when IR::ExpressionStmt if statement.expression.is_a?(IR::Assignment) emit_assignment_expression(statement.expression, indent) elsif statement.expression.is_a?(IR::Call) ["#{indent}#{emit_expression(statement.expression)};"] else # A pure-value expression discarded as a statement (e.g. the # `T<-x` touch idiom) needs a void cast, otherwise the value # is unused and triggers -Wunused-value. ["#{indent}(void)(#{emit_expression(statement.expression)});"] end when IR::ReturnStmt if statement.value if array_type?(function.return_type) emit_array_return(statement.value, indent) else ["#{indent}return #{emit_expression(statement.value)};"] end else ["#{indent}return;"] end when IR::WhileStmt body_continue_label = loop_continue_label_name(statement.body) body = body_continue_label ? statement.body[0...-1] : statement.body body_break_label = loop_break_label_name(body, remaining_statements) @suppressed_labels << body_break_label if body_break_label && !statements_need_explicit_break_label_after_emission?(body, body_break_label, loop_break_label_active: true) if @debug_guards @loop_guard_id += 1 guard_name = "__mt_loop_#{@loop_guard_id}" lines = ["#{indent}{ uintptr_t #{guard_name} = 0;"] lines << "#{indent}while (#{emit_expression(statement.condition)}) {" guard = "#{INDENT * (level + 1)}if (++#{guard_name} > #{LOOP_GUARD_MAX_ITERATIONS}) mt_fatal(\"loop iteration limit exceeded in #{function.linkage_name}\");" lines << guard lines.concat(emit_statement_sequence(body, level + 1, function:, used_labels:, loop_continue_label: body_continue_label, loop_break_label: body_break_label)) lines << "#{indent}}" lines << "#{indent}}" lines else lines = ["#{indent}while (#{emit_expression(statement.condition)}) {"] lines.concat(emit_statement_sequence(body, level + 1, function:, used_labels:, loop_continue_label: body_continue_label, loop_break_label: body_break_label)) lines << "#{indent}}" lines end when IR::ForStmt body_continue_label = loop_continue_label_name(statement.body) body = body_continue_label ? statement.body[0...-1] : statement.body body_break_label = loop_break_label_name(body, remaining_statements) @suppressed_labels << body_break_label if body_break_label && !statements_need_explicit_break_label_after_emission?(body, body_break_label, loop_break_label_active: true) if @debug_guards @loop_guard_id += 1 guard_name = "__mt_loop_#{@loop_guard_id}" lines = ["#{indent}{ uintptr_t #{guard_name} = 0;"] lines << "#{indent}for (#{emit_for_clause_statement(statement.init)}; #{emit_expression(statement.condition)}; #{emit_for_clause_statement(statement.post)}) {" guard = "#{INDENT * (level + 1)}if (++#{guard_name} > #{LOOP_GUARD_MAX_ITERATIONS}) mt_fatal(\"loop iteration limit exceeded in #{function.linkage_name}\");" lines << guard lines.concat(emit_statement_sequence(body, level + 1, function:, used_labels:, loop_continue_label: body_continue_label, loop_break_label: body_break_label)) lines << "#{indent}}" lines << "#{indent}}" lines else lines = ["#{indent}for (#{emit_for_clause_statement(statement.init)}; #{emit_expression(statement.condition)}; #{emit_for_clause_statement(statement.post)}) {"] lines.concat(emit_statement_sequence(body, level + 1, function:, used_labels:, loop_continue_label: body_continue_label, loop_break_label: body_break_label)) lines << "#{indent}}" lines end when IR::BreakStmt ["#{indent}break;"] when IR::ContinueStmt ["#{indent}continue;"] when IR::GotoStmt return ["#{indent}continue;"] if loop_continue_label && statement.label == loop_continue_label return ["#{indent}break;"] if loop_break_label && statement.label == loop_break_label ["#{indent}goto #{statement.label};"] when IR::LabelStmt return [] if @suppressed_labels.include?(statement.name) return [] if loop_continue_label && statement.name == loop_continue_label return [] unless used_labels.include?(statement.name) ["#{indent}#{statement.name}:;"] when IR::StaticAssert ["#{indent}#{emit_static_assert(statement)}"] when IR::IfStmt emit_if_statement(statement, level, function:, used_labels:, loop_continue_label:, loop_break_label:) when IR::SwitchStmt if switch_emittable_as_if?(statement, loop_break_label:) emit_switch_as_if_statement(statement, level, function:, used_labels:, loop_continue_label:, loop_break_label:) else lines = ["#{indent}switch (#{emit_expression(statement.expression)}) {"] has_default = false statement.cases.each do |switch_case| if switch_case.is_a?(IR::SwitchDefaultCase) has_default = true lines << "#{indent}#{INDENT}default: {" else lines << "#{indent}#{INDENT}case #{emit_expression(switch_case.value)}: {" end lines.concat(emit_statement_sequence(switch_case.body, level + 2, function:, used_labels:, loop_continue_label:)) lines << "#{indent}#{INDENT}#{INDENT}break;" unless body_terminates?(switch_case.body) lines << "#{indent}#{INDENT}}" end # An exhaustive enum/variant match covers every arm, so the C # `default` path is unreachable. Emitting it lets the compiler # prove non-void functions always return, avoiding spurious # -Wreturn-type warnings on the implicit no-match path. if statement.exhaustive && !has_default lines << "#{indent}#{INDENT}default: __builtin_unreachable();" end lines << "#{indent}}" lines end else raise CBackendError.new("unsupported IR statement #{statement.class.name}", line: 0, column: 0, path: @path) end end alias_lines + line_directive + statement_lines end |
#emit_statement_sequence(statements, level, function:, used_labels:, loop_continue_label: nil, loop_break_label: nil) ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 6 def emit_statement_sequence(statements, level, function:, used_labels:, loop_continue_label: nil, loop_break_label: nil) statements.each_with_index.flat_map do |statement, index| emit_statement( statement, level, function:, used_labels:, loop_continue_label:, loop_break_label:, remaining_statements: statements[(index + 1)..] || [], ) end end |
#emit_static_assert(statement) ⇒ Object
550 551 552 553 554 555 556 557 558 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 550 def emit_static_assert(statement) = if statement..is_a?(IR::StringLiteral) statement..value.inspect else emit_expression(statement.) end "_Static_assert(#{emit_expression(statement.condition)}, #{});" end |
#fold_single_use_bool_if_temp(local_decl, if_stmt, remaining_statements) ⇒ Object
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 283 def fold_single_use_bool_if_temp(local_decl, if_stmt, remaining_statements) return unless local_decl.is_a?(IR::LocalDecl) return unless if_stmt.is_a?(IR::IfStmt) return unless bool_type?(local_decl.type) condition_kind = single_use_bool_if_condition_kind(if_stmt.condition, local_decl.linkage_name) return unless condition_kind return unless name_reference_count_in_statements(if_stmt.then_body, local_decl.linkage_name).zero? return unless name_reference_count_in_statements(if_stmt.else_body || [], local_decl.linkage_name).zero? return unless name_reference_count_in_statements(remaining_statements, local_decl.linkage_name).zero? condition = if condition_kind == :direct local_decl.value else IR::Unary.new(operator: "not", operand: local_decl.value, type: local_decl.type) end IR::IfStmt.new(condition:, then_body: if_stmt.then_body, else_body: if_stmt.else_body) end |
#fold_single_use_local_alias(source_decl, alias_decl, remaining_statements) ⇒ Object
267 268 269 270 271 272 273 274 275 276 277 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 267 def fold_single_use_local_alias(source_decl, alias_decl, remaining_statements) return unless source_decl.is_a?(IR::LocalDecl) return unless alias_decl.is_a?(IR::LocalDecl) return unless compiler_generated_local_name?(source_decl.linkage_name) return if array_type?(source_decl.type) || array_type?(alias_decl.type) return unless source_decl.type == alias_decl.type return unless alias_decl.value.is_a?(IR::Name) && alias_decl.value.name == source_decl.linkage_name return unless name_reference_count_in_statements(remaining_statements, source_decl.linkage_name).zero? IR::LocalDecl.new(name: alias_decl.name, linkage_name: alias_decl.linkage_name, type: alias_decl.type, value: source_decl.value) end |
#fresh_checked_index_alias_name ⇒ Object
506 507 508 509 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 506 def fresh_checked_index_alias_name @checked_index_alias_id += 1 "__mt_checked_index_ptr_#{@checked_index_alias_id}" end |
#hoistable_checked_index_alias?(expression) ⇒ Boolean
454 455 456 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 454 def hoistable_checked_index_alias?(expression) side_effect_free_expression?(expression.receiver) && side_effect_free_expression?(expression.index) end |
#name_reference_count_in_expression(expression, name) ⇒ Object
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 349 def name_reference_count_in_expression(expression, name) case expression when IR::Name expression.name == name ? 1 : 0 when IR::Member name_reference_count_in_expression(expression.receiver, name) when IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex name_reference_count_in_expression(expression.receiver, name) + name_reference_count_in_expression(expression.index, name) when IR::Call callee_count = expression.callee.is_a?(String) ? 0 : name_reference_count_in_expression(expression.callee, name) callee_count + expression.arguments.sum { |argument| name_reference_count_in_expression(argument, name) } when IR::Unary name_reference_count_in_expression(expression.operand, name) when IR::Binary name_reference_count_in_expression(expression.left, name) + name_reference_count_in_expression(expression.right, name) when IR::Conditional name_reference_count_in_expression(expression.condition, name) + name_reference_count_in_expression(expression.then_expression, name) + name_reference_count_in_expression(expression.else_expression, name) when IR::ReinterpretExpr name_reference_count_in_expression(expression.expression, name) when IR::AddressOf, IR::Cast name_reference_count_in_expression(expression.expression, name) when IR::AggregateLiteral expression.fields.sum { |field| name_reference_count_in_expression(field.value, name) } when IR::ArrayLiteral expression.elements.sum { |element| name_reference_count_in_expression(element, name) } when IR::VariantLiteral expression.fields.sum { |field| name_reference_count_in_expression(field.value, name) } else 0 end end |
#name_reference_count_in_statement(statement, name) ⇒ Object
317 318 319 320 321 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/statements.rb', line 317 def name_reference_count_in_statement(statement, name) case statement when IR::LocalDecl name_reference_count_in_expression(statement.value, name) when IR::Assignment name_reference_count_in_expression(statement.target, name) + name_reference_count_in_expression(statement.value, name) when IR::BlockStmt, IR::WhileStmt, IR::ForStmt count = name_reference_count_in_statements(statement.body, name) return count unless statement.is_a?(IR::WhileStmt) || statement.is_a?(IR::ForStmt) count += name_reference_count_in_expression(statement.condition, name) count += name_reference_count_in_statement(statement.init, name) if statement.is_a?(IR::ForStmt) count += name_reference_count_in_statement(statement.post, name) if statement.is_a?(IR::ForStmt) count when IR::IfStmt name_reference_count_in_expression(statement.condition, name) + name_reference_count_in_statements(statement.then_body, name) + name_reference_count_in_statements(statement.else_body || [], name) when IR::SwitchStmt name_reference_count_in_expression(statement.expression, name) + statement.cases.sum { |switch_case| (switch_case.is_a?(IR::SwitchCase) ? name_reference_count_in_expression(switch_case.value, name) : 0) + name_reference_count_in_statements(switch_case.body, name) } when IR::StaticAssert name_reference_count_in_expression(statement.condition, name) + name_reference_count_in_expression(statement., name) when IR::ReturnStmt statement.value ? name_reference_count_in_expression(statement.value, name) : 0 when IR::ExpressionStmt name_reference_count_in_expression(statement.expression, name) else 0 end end |
#name_reference_count_in_statements(statements, name) ⇒ Object
313 314 315 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 313 def name_reference_count_in_statements(statements, name) statements.sum { |statement| name_reference_count_in_statement(statement, name) } end |
#side_effect_free_expression?(expression) ⇒ Boolean
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 458 def side_effect_free_expression?(expression) case expression when IR::Name, IR::IntegerLiteral, IR::FloatLiteral, IR::StringLiteral, IR::BooleanLiteral, IR::NullLiteral, IR::ZeroInit, IR::SizeofExpr, IR::AlignofExpr, IR::OffsetofExpr true when IR::Member side_effect_free_expression?(expression.receiver) when IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex side_effect_free_expression?(expression.receiver) && side_effect_free_expression?(expression.index) when IR::Unary side_effect_free_expression?(expression.operand) when IR::Binary side_effect_free_expression?(expression.left) && side_effect_free_expression?(expression.right) when IR::Conditional side_effect_free_expression?(expression.condition) && side_effect_free_expression?(expression.then_expression) && side_effect_free_expression?(expression.else_expression) when IR::ReinterpretExpr, IR::AddressOf, IR::Cast side_effect_free_expression?(expression.expression) when IR::AggregateLiteral expression.fields.all? { |field| side_effect_free_expression?(field.value) } when IR::VariantLiteral expression.fields.all? { |field| side_effect_free_expression?(field.value) } when IR::ArrayLiteral expression.elements.all? { |element| side_effect_free_expression?(element) } when IR::Call false else false end end |
#single_use_bool_if_condition_kind(condition, temp_name) ⇒ Object
303 304 305 306 307 308 309 310 311 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 303 def single_use_bool_if_condition_kind(condition, temp_name) return :direct if condition.is_a?(IR::Name) && condition.name == temp_name if condition.is_a?(IR::Unary) && condition.operator == "not" && condition.operand.is_a?(IR::Name) && condition.operand.name == temp_name return :negated end nil end |
#transform_compactable_nested_bodies(statement) ⇒ Object
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 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 229 def transform_compactable_nested_bodies(statement) case statement when IR::BlockStmt IR::BlockStmt.new(body: compact_generated_statement_sequence(statement.body)) when IR::WhileStmt canonicalize_top_guarded_while( IR::WhileStmt.new(condition: statement.condition, body: compact_generated_statement_sequence(statement.body)) ) when IR::ForStmt IR::ForStmt.new( init: statement.init, condition: statement.condition, post: statement.post, body: compact_generated_statement_sequence(statement.body), ) when IR::IfStmt IR::IfStmt.new( condition: statement.condition, then_body: compact_generated_statement_sequence(statement.then_body), else_body: statement.else_body ? compact_generated_statement_sequence(statement.else_body) : nil, ) when IR::SwitchStmt IR::SwitchStmt.new( expression: statement.expression, exhaustive: statement.exhaustive, cases: statement.cases.map do |switch_case| if switch_case.is_a?(IR::SwitchDefaultCase) IR::SwitchDefaultCase.new(body: compact_generated_statement_sequence(switch_case.body)) else IR::SwitchCase.new(value: switch_case.value, body: compact_generated_statement_sequence(switch_case.body)) end end, ) else statement end end |
#with_checked_index_aliases(aliases) ⇒ Object
511 512 513 514 515 516 |
# File 'lib/milk_tea/core/c_backend/statements.rb', line 511 def with_checked_index_aliases(aliases) @checked_index_alias_stack << aliases yield ensure @checked_index_alias_stack.pop end |