Esempio n. 1
0
def codegen_each(statement: ast.Each, oc: OuterContext) -> InnerContext:
    """
    Specialized variant of codegen_statement for for-each statements.
    """
    # The Inner Context we will return. Note that an each loop never ends.
    ic = InnerContext(immediate_state="0")
    # We build the code for the inner body of the for-each loop.
    inner_ic = codegen_statement(statement.statement, oc)
    # We build the code for the reset expression.
    inner_expression = codegen_expression(statement.expression)
    # for-each statements don't use the inner_immediate states in any particular
    # way.
    ic.inherit(inner_ic)
    # The each loops don't have any state, but we have to build a bit of code
    # for the reset.
    ic.source_code += oc.indent_str + f"if ({inner_expression}) {{\n"
    # We have to reset all the states owned by the body of the loop.
    for state in ic.owned_states:
        ic.source_code += oc.indent_str + "\t" + f"{state} = 0;\n"
    # Closing the reset statement and beginning the statement of the body.
    ic.source_code += oc.indent_str + "}\n"
    # Adding the code for the inner body.
    ic.source_code += inner_ic.source_code
    # Returning the source code, the immediate states and the owned states.
    return ic
Esempio n. 2
0
def codegen_par(statement: ast.Par, oc: OuterContext) -> InnerContext:
    """
    Specialized variant of codegen_statement for par statements.
    """
    # The Inner Context we will return.
    ic = InnerContext()
    # We also keep track of all the immediate states of our sub statements to
    # build our own immediate state at the end.
    immediate_states: List[str] = list()
    # Par statements are mostly invisible in the produced code, they look like
    # normal C execution.
    # We build some code for each of the inner parallel statements.
    for index, inner_statement in enumerate(statement.statements):
        # We build the code for the inner body of the par statement. We don't
        # need to indent the code.
        inner_ic = codegen_statement(inner_statement, oc)
        # We inherit the recursive values from the InnerContext.
        ic.inherit(inner_ic)
        # We also keep track of the immediate state.
        immediate_states.append(inner_ic.immediate_state)
        # We add the source code for the statement executed sequentially.
        ic.source_code += inner_ic.source_code
    # We return the expected source code. The single immediate state of a par
    # statement is the intersection of all the immediate states of its children
    # (i.e. the parallel execution ends when all the threads end).
    ic.immediate_state = "(" + " && ".join(immediate_states) + ")"
    return ic
Esempio n. 3
0
def codegen_local(statement: ast.EmitStatement,
                  oc: OuterContext) -> InnerContext:
    """
    Specialized variant of codegen_statement for local statements.
    """
    # The Inner Context we will return.
    ic = InnerContext()
    # We add a new local to our InnerContext based on the name provided in the
    # source code.
    ic.owned_locals.append(statement.name)
    # There is no code associated with a local statement.
    # We return the expected triple. A local statement always exits instantly.
    ic.immediate_state = "1"
    return ic
Esempio n. 4
0
def codegen_emit(statement: ast.EmitStatement,
                 oc: OuterContext) -> InnerContext:
    """
    Specialized variant of codegen_statement for emit statements.
    """
    # The Inner Context we will return.
    ic = InnerContext()
    # We get the C code for the inner expression (the rvalue).
    inner_expression = codegen_expression(statement.expression)
    # We build the expected source code.
    ic.source_code = (oc.indent_str +
                      f"*{statement.signal} = {inner_expression};\n")
    # We return the expected triple. An emit statement always exits instantly.
    ic.immediate_state = "1"
    return ic
Esempio n. 5
0
def codegen_submodule(statement: ast.Submodule,
                      oc: OuterContext) -> InnerContext:
    """
    Specialized variant of codegen_statement for submodule statements.
    """
    # The inner context we will return.
    ic = InnerContext()
    # All the argument signals are already pointers, which is what the callee
    # expects, hence we may directly build the code for the arguments.
    code_arguments = ", ".join([str(signal) for signal in statement.arguments])
    # We then simply call the the C function associated with the module by name.
    ic.source_code += f"{oc.indent_str}{statement.name}({code_arguments});\n"
    # A submodule call is always instantaneous, like an emit.
    ic.immediate_state = "1"
    # We add the new submodule to the owned submodules.
    ic.owned_submodules.append(statement.name)
    # We return the newly built context.
    return ic
Esempio n. 6
0
def codegen_await(statement: ast.AwaitStatement,
                  oc: OuterContext) -> InnerContext:
    """
    Specialized variant of codegen_statement for await statements.
    """
    # The Inner Context we will return.
    ic = InnerContext()
    # We build the code for the inner expression.
    inner_expression = codegen_expression(statement.expression)
    # We need a new state for the await statement.
    await_state = SSAGenerator.new_name("await")
    # We build the expected source code.
    ic.source_code = oc.indent_str + f"{await_state} |= {inner_expression};\n"
    # Our immediate state is given by the "await_state".
    ic.immediate_state = await_state
    # We own the new single state associated with the await statement.
    ic.owned_states.append(await_state)
    return ic
Esempio n. 7
0
def codegen_seq(statement: ast.Seq, oc: OuterContext) -> InnerContext:
    """
    Specialized variant of codegen_statement for seq statements.
    """
    # The Inner Context we will return.
    ic = InnerContext()
    # We need a state to keep track of the sequential execution.
    seq_state = SSAGenerator.new_name("seq")
    # We add our new state to the list of states we need to keep track of.
    ic.owned_states.append(seq_state)
    # We build some code for each of the inner sequential statements. We first
    # have to create the context for those future statement.
    inner_oc = OuterContext(indent=oc.indent + 1)
    for index, inner_statement in enumerate(statement.statements):
        # We build the code for the inner body of the seq statement.
        inner_ic = codegen_statement(inner_statement, inner_oc)
        # We inherit the recursive values from the InnerContext.
        ic.inherit(inner_ic)
        # We add the code for the case. Note that we always use ifs (and never
        # else) to fall to the next case whenever possible.
        ic.source_code += oc.indent_str + f"if ({seq_state} == {index}) {{\n"
        # We add the source code for the statement executed sequentially.
        ic.source_code += inner_ic.source_code
        # EDGE CASE
        # If we have reached the last state of the sequential execution, we
        # don't jump.
        if index + 1 == len(statement.statements):
            ic.source_code += (oc.indent_str + "\t" +
                               "/* LAST SEQUENTIAL STATE */\n")
        else:
            # We move to the next state if all the immediate states of the inner
            # statement are true, or if there are no immediate states.
            ic.source_code += (oc.indent_str + "\t" +
                               f"{seq_state} += {inner_ic.immediate_state};\n")
        # Closing the if statement.
        ic.source_code += oc.indent_str + "}\n"
    # We return the expected source code. The single immediate state of a seq
    # statement is whether it reached its last state.
    ic.immediate_state = (f"({seq_state} == {len(statement.statements) - 1}", )
    return ic
Esempio n. 8
0
def codegen_if(statement: ast.IfStatement, oc: OuterContext) -> InnerContext:
    """
    Specialized variant of codegen_statement for if-else statements.
    """
    # The Inner Context we will return.
    ic = InnerContext()
    # We start by building the code for our conditional expression.
    inner_expression = codegen_expression(statement.expression)
    # We also build the code for our indented if statement.
    inner_ic = codegen_statement(statement.statement,
                                 OuterContext(oc.indent + 1))
    # Inheriting the owned and local of our inner expression.
    ic.inherit(inner_ic)
    # We start by filling the code for the if branch before checking if there is
    # an else.
    ic.source_code += f"{oc.indent_str}if ({inner_expression}) {{\n"
    ic.source_code += inner_ic.source_code
    ic.source_code += f"{oc.indent_str}}}\n"
    if statement.else_statement is None:
        # No else statement, this is the easy case. The immediate state of the
        # if statement is that of its if branch if it is taken, otherwise it
        # exits immediately.
        ic.immediate_state = (
            f"(!{inner_expression} || {inner_ic.immediate_state} * "
            f"{inner_expression})")
        # We simply return the InnerContext we have built so far.
        return ic
    else:
        # We must add the code for the else statement.
        else_ic = codegen_statement(statement.else_statement,
                                    OuterContext(oc.indent + 1))
        # We also inherit the InnerContext from the else branch.
        ic.inherit(else_ic)
        # We add the code for the else branch.
        ic.source_code += (f"{oc.indent_str}else {{\n"
                           f"{else_ic.source_code}"
                           f"{oc.indent_str}}}\n")
        # We return the Inner Context we have built.
        return ic