# encoding: utf-8 # frozen_string_literal: true module Carbon module Tacky # Defines a "block" in Tacky. This corresponds to LLVM's "basic block" for # functions. This only contains a list of instructions and a name, and # can be referenced by that name. class Block # A list of instructions that are executed by this block. This # shoudln't be directly modified, and instead should be modified via # {#build}. # # @return [] attr_reader :instructions # The "name" of the block. This is used to make the resulting LLVM IR # more legible. # # @return [::String] attr_reader :name # Temporarily used during building to store the mapping of instruction # ids to their LLVM counterparts. # # @return [{::Numeric => ::LLVM::Value}] attr_reader :mapping # Initializes the block with the given name and instructions. If # no name is given, it defaults to an empty string (`""`). Instructions # should not be passed by initialization. # # @param function [Tacky::Function] The function this block is a part # of. # @param name [::String] The name of the block. # @param instructions [] This should not be used. def initialize(function, name = "", instructions = []) @function = function @name = name @instructions = instructions end # Increment the function's counter, returning the next valid instruction # id. # # @return [::Numeric] def next @function.counter.increment end # Retrieves the dependencies that the block has. This is determined by # asking any of the instructions if they have any dependencies, returning # an empty set if all of them have none. # # @api private # @see Function#dependencies # @see Instruction#dependencies # @return [Set] The dependencies of the block. def dependencies @instructions.map(&:dependencies).inject(Set.new, :merge) end # Builds the block with the given context. This iterates over all of # the instructions, calling them, and storing the results in the function # instruction table ({Tacky::Context#instructions}). # # @api private # @see Instruction#call # @see Context#instructions # @param context [Tacky::Context] The context to build with. # @return [void] def call(context) this = context.blocks.fetch(self) @instructions.each do |inst| context.instructions[inst.value] = inst.call(context, this) end end # Builds the block. If a block is given, it yields a {Tacky::Builder}; # otherwise, it returns one. # # @yield [builder] To build the block. # @yieldparam builder [Tacky::Builder] The builder. # @return [Tacky::Builder] If no block was given. # @return [::Object] Otherwise. def build if block_given? yield Tacky::Builder.new(self) else Tacky::Builder.new(self) end end end end end