Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use of delayed and physical connections as provided by the C++ runtime #1583

Merged
merged 33 commits into from
Feb 16, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
1bd0fa4
adjusting for port interface changes
tanneberger Jan 13, 2023
00cba56
don't apply the after delay transformation
cmnrd Jan 17, 2023
f1e6228
update reactor-cpp
cmnrd Jan 17, 2023
22a5fc6
generate simple delayed connections
cmnrd Jan 17, 2023
d2efc42
don't bind delayed connections directly
cmnrd Jan 17, 2023
d17d5c6
Merge branch 'master' into cpp-native-delayed-connections
cmnrd Jan 31, 2023
b115bbf
Revert "adjusting for port interface changes"
cmnrd Jan 31, 2023
0bbc2c5
update port constructor call
cmnrd Jan 31, 2023
4772fe5
update reactor-cpp
cmnrd Jan 31, 2023
8e78170
adjust to changes in the DelayedConnection class
cmnrd Feb 1, 2023
fb27ca9
cleanup
cmnrd Feb 1, 2023
8b74d29
update reactor-cpp and make sure all connections are generated
cmnrd Feb 1, 2023
3d3a032
delete CppDelayBodyGenerator
cmnrd Feb 1, 2023
946cada
add testcase for spares usage of multiports
cmnrd Feb 2, 2023
b5f0a22
implement after delays for multiport connections
cmnrd Feb 2, 2023
f200259
update reactor-cpp
cmnrd Feb 2, 2023
443e957
implement physical connections
cmnrd Feb 2, 2023
4f9fef2
make physical connections over multiports work
cmnrd Feb 2, 2023
abe9c42
make after and physical connections work in combination with generic …
cmnrd Feb 2, 2023
9f3bbe5
add testcase for checking multiports with set() called multiple times
cmnrd Feb 3, 2023
0a0cc22
update reactor-cpp
cmnrd Feb 3, 2023
bdb2b3e
reaction views on banks should be vectors
cmnrd Feb 6, 2023
7f50523
mark the PhysicalConnection C test as failing
cmnrd Feb 6, 2023
46f50d9
format test
cmnrd Feb 6, 2023
fc03a65
update cli test script
cmnrd Feb 6, 2023
da8b494
update reactor-cpp
cmnrd Feb 6, 2023
f98d741
correct types in lfutil.hh
cmnrd Feb 6, 2023
3306728
update reactor-cpp
cmnrd Feb 6, 2023
b06376b
declare connections after ports
cmnrd Feb 6, 2023
4cb0758
Merge branch 'master' into cpp-native-delayed-connections
cmnrd Feb 8, 2023
0bac517
merging master
tanneberger Feb 10, 2023
e291977
Update org.lflang/src/org/lflang/generator/cpp/CppConnectionGenerator.kt
lhstrh Feb 16, 2023
ab569f4
Update org.lflang/src/org/lflang/generator/cpp/CppAssembleMethodGener…
lhstrh Feb 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions org.lflang/src/lib/cpp/lfutil.hh
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,42 @@ void bind_multiple_ports(
}
}

template<class T>
void bind_multiple_connections_with_ports(
std::vector<reactor::Connection<T>*>& connections,
std::vector<reactor::Port<T>*>& ports,
bool repeat_left) {

if (repeat_left) {
size_t l_size = connections.size();
cmnrd marked this conversation as resolved.
Show resolved Hide resolved
size_t r_size = ports.size();
// divide and round up
size_t repetitions = r_size / l_size + (r_size % l_size != 0);
// repeat repetitions-1 times
connections.reserve(repetitions * l_size);
for (size_t i = 1; i < repetitions; i++) {
std::copy_n(connections.begin(), l_size, std::back_inserter(connections));
}
}

auto left_it = connections.begin();
auto right_it = ports.begin();

if (connections.size() < ports.size()) {
reactor::log::Warn() << "There are more right ports than left ports. "
<< "Not all ports will be connected!";
} else if (connections.size() > ports.size()) {
reactor::log::Warn() << "There are more left ports than right ports. "
<< "Not all ports will be connected!";
}

while (left_it != connections.end() && right_it != ports.end()) {
auto left = *left_it;
auto right = *right_it;
left->bind_downstream_port(right);
left_it++;
right_it++;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@

package org.lflang.generator.cpp

import org.lflang.*
import org.lflang.generator.PrependOperator
import org.lflang.hasMultipleConnections
import org.lflang.isBank
import org.lflang.isMultiport
import org.lflang.joinWithLn
import org.lflang.generator.cpp.CppConnectionGenerator.Companion.name
import org.lflang.generator.cpp.CppConnectionGenerator.Companion.requiresConnectionClass
import org.lflang.generator.cpp.CppPortGenerator.Companion.dataType
import org.lflang.lf.Action
import org.lflang.lf.Connection
import org.lflang.lf.ParameterReference
Expand Down Expand Up @@ -143,22 +143,25 @@ class CppAssembleMethodGenerator(private val reactor: Reactor) {
}
}

private fun declareConnection(c: Connection, idx: Int): String {
return if (c.hasMultipleConnections) {
private fun declareConnection(c: Connection, idx: Int): String =
if (c.hasMultipleConnections) {
declareMultiportConnection(c, idx)
} else {
val leftPort = c.leftPorts[0]
val rightPort = c.rightPorts[0]

"""
if (c.requiresConnectionClass)
"""
// connection $idx
${c.name}.bind_upstream_port(&${leftPort.name});
${c.name}.bind_downstream_port(&${rightPort.name});
""".trimIndent()
else
"""
// connection $idx
${leftPort.name}.bind_to(&${rightPort.name});
""".trimIndent()
}
}

private val VarRef.isMultiport get() = (variable as? Port)?.isMultiport == true
private val VarRef.isInBank get() = container?.isBank == true

/**
* Return the C++ type of a port.
Expand All @@ -167,30 +170,49 @@ class CppAssembleMethodGenerator(private val reactor: Reactor) {
* complex logic for finding the actual type, we return a decltype statement and let the C++ compiler do the job.
*/
private val VarRef.portType: String
get() = when {
isInBank && isMultiport -> "reactor::Port<std::remove_reference<decltype(${container.name}[0]->${variable.name}[0])>::type::value_type>*"
isInBank && !isMultiport -> "reactor::Port<std::remove_reference<decltype(${container.name}[0]->${variable.name})>::type::value_type>*"
!isInBank && isMultiport -> "reactor::Port<std::remove_reference<decltype($name[0])>::type::value_type>*"
else -> "reactor::Port<std::remove_reference<decltype($name)>::type::value_type>*"
}
get() = "reactor::Port<${dataType}>*"

private fun declareMultiportConnection(c: Connection, idx: Int): String {
// It should be safe to assume that all ports have the same type. Thus we just pick the
// first left port to determine the type of the entire connection
val portType = c.leftPorts[0].portType

// TODO this does not work with generics
lhstrh marked this conversation as resolved.
Show resolved Hide resolved
lhstrh marked this conversation as resolved.
Show resolved Hide resolved
lhstrh marked this conversation as resolved.
Show resolved Hide resolved
val leftPort = c.leftPorts[0].variable as Port
val dataType = leftPort.inferredType.cppType

// Generate code which adds all left hand ports and all right hand ports to a vector each. If we are handling multiports
// within a bank, then we normally iterate over all banks in an outer loop and over all ports in an inner loop. However,
// if the connection is a cross connection, than we change the order on the right side and iterate over ports before banks.
return with(PrependOperator) {
"""
|// connection $idx
|std::vector<$portType> __lf_left_ports_$idx;
${" |"..c.leftPorts.joinWithLn { addAllPortsToVector(it, "__lf_left_ports_$idx") }}
|std::vector<$portType> __lf_right_ports_$idx;
${" |"..c.rightPorts.joinWithLn { addAllPortsToVector(it, "__lf_right_ports_$idx") }}
|lfutil::bind_multiple_ports(__lf_left_ports_$idx, __lf_right_ports_$idx, ${c.isIterated});
""".trimMargin()
if (!c.requiresConnectionClass) {
"""
|// connection $idx
|std::vector<$portType> __lf_left_ports_$idx;
${" |"..c.leftPorts.joinWithLn { addAllPortsToVector(it, "__lf_left_ports_$idx") }}
|std::vector<$portType> __lf_right_ports_$idx;
${" |"..c.rightPorts.joinWithLn { addAllPortsToVector(it, "__lf_right_ports_$idx") }}
|lfutil::bind_multiple_ports(__lf_left_ports_$idx, __lf_right_ports_$idx, ${c.isIterated});
""".trimMargin()
} else {
"""
|// connection $idx
|std::vector<$portType> __lf_left_ports_$idx;
${" |"..c.leftPorts.joinWithLn { addAllPortsToVector(it, "__lf_left_ports_$idx") }}
|${c.name}.reserve(__lf_left_ports_$idx.size());
|for(size_t __lf_idx{0}; __lf_idx < __lf_left_ports_$idx.size(); __lf_idx++) {
| ${c.name}.emplace_back("${c.name}" + std::to_string(__lf_idx), this, ${c.delay.toCppTime()});
| ${c.name}.back().bind_upstream_port(__lf_left_ports_$idx[__lf_idx]);
|}
|std::vector<reactor::Connection<$dataType>*> __lf_connection_pointers_$idx;
|for (auto& connection : ${c.name}) {
| __lf_connection_pointers_$idx.push_back(&connection);
|}
|std::vector<$portType> __lf_right_ports_$idx;
${" |"..c.rightPorts.joinWithLn { addAllPortsToVector(it, "__lf_right_ports_$idx") }}
|lfutil::bind_multiple_connections_with_ports(__lf_connection_pointers_$idx, __lf_right_ports_$idx, ${c.isIterated});
""".trimMargin()
}
}
}

Expand Down
59 changes: 59 additions & 0 deletions org.lflang/src/org/lflang/generator/cpp/CppConnectionGenerator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.lflang.generator.cpp

import org.lflang.*
import org.lflang.generator.cpp.CppConnectionGenerator.Companion.cppType
import org.lflang.generator.cpp.CppPortGenerator.Companion.dataType
import org.lflang.lf.Connection
import org.lflang.lf.Port
import org.lflang.lf.Reactor
import org.lflang.lf.VarRef

class CppConnectionGenerator(private val reactor: Reactor) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class needs some documentation...


companion object {
val Connection.name: String
get() =
"connection_" + leftPorts.joinToString("__") {
if (it.container == null) {
it.variable.name
} else {
it.container.name + "_" + it.variable.name
}
}

val Connection.cppType: String
get() {
val leftPort = leftPorts.first()
return when {
isPhysical -> "reactor::PhysicalConnection<${leftPort.dataType}>"
delay != null -> "reactor::DelayedConnection<${leftPort.dataType}>"
else -> throw IllegalArgumentException("Connection is neither physical nor delayed")
}
}

val Connection.requiresConnectionClass: Boolean get() = isPhysical || delay != null
}

fun generateDeclarations() =
reactor.connections.mapNotNull { generateDecleration(it) }
.joinToString("\n", "// connections \n", postfix = "\n")

fun generateInitializers() =
reactor.connections.mapNotNull { generateConstructorInitializer(it) }.joinLn()

private fun generateDecleration(connection: Connection): String? =
if (connection.requiresConnectionClass) {
if (connection.hasMultipleConnections) {
"std::vector<${connection.cppType}> ${connection.name};"
} else {
"${connection.cppType} ${connection.name};"
}
} else null

private fun generateConstructorInitializer(connection: Connection): String? =
if (connection.requiresConnectionClass && !connection.hasMultipleConnections) {
val delay = connection.delay.toCppTime()
val name = connection.name
""", $name{"$name", this, $delay}"""
} else null
}
lhstrh marked this conversation as resolved.
Show resolved Hide resolved
45 changes: 0 additions & 45 deletions org.lflang/src/org/lflang/generator/cpp/CppDelayBodyGenerator.kt

This file was deleted.

4 changes: 2 additions & 2 deletions org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ fun Expression.toCppCode(inferredType: InferredType? = null): String =
* @param outerContext A flag indicating whether to generate code for the scope of the outer reactor class.
* This should be set to false if called from code generators for the inner class.
*/
fun Expression.toCppTime(): String =
this.toCppCode(inferredType = InferredType.time())
fun Expression?.toCppTime(): String =
this?.toCppCode(inferredType = InferredType.time()) ?: "reactor::Duration::zero()"

/** Get the textual representation of a width in C++ code */
fun WidthSpec.toCppCode(): String = terms.joinToString(" + ") {
Expand Down
3 changes: 0 additions & 3 deletions org.lflang/src/org/lflang/generator/cpp/CppGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ package org.lflang.generator.cpp
import org.eclipse.emf.ecore.resource.Resource
import org.lflang.ErrorReporter
import org.lflang.Target
import org.lflang.ast.AfterDelayTransformation
import org.lflang.generator.CodeMap
import org.lflang.generator.GeneratorBase
import org.lflang.generator.GeneratorResult
Expand Down Expand Up @@ -67,8 +66,6 @@ class CppGenerator(
}

override fun doGenerate(resource: Resource, context: LFGeneratorContext) {
// Register the after delay transformation to be applied by GeneratorBase.
registerTransformation(AfterDelayTransformation(CppDelayBodyGenerator, CppTypes, resource))
super.doGenerate(resource, context)

if (!canGenerate(errorsOccurred(), mainDef, errorReporter, context)) return
Expand Down
28 changes: 23 additions & 5 deletions org.lflang/src/org/lflang/generator/cpp/CppPortGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,33 @@
package org.lflang.generator.cpp

import org.lflang.inferredType
import org.lflang.isBank
import org.lflang.isMultiport
import org.lflang.joinWithLn
import org.lflang.lf.Input
import org.lflang.lf.Output
import org.lflang.lf.Port
import org.lflang.lf.Reactor
import org.lflang.lf.*

class CppPortGenerator(private val reactor: Reactor) {

companion object {
private val VarRef.isMultiport get() = (variable as? Port)?.isMultiport == true
private val VarRef.isInBank get() = container?.isBank == true

/**
* Return the C++ type of a port reference.
*
* We cannot easily infer this type directly, because it might be used within a generic reactor but the reference is
* likely used outside of it. Instead of implementing complex logic for finding the actual type, we return a decltype
* statement and let the C++ compiler do the job.
*/
val VarRef.dataType: String
get() = when {
isInBank && isMultiport -> "std::remove_reference<decltype(${container.name}[0]->${variable.name}[0])>::type::value_type"
isInBank && !isMultiport -> "std::remove_reference<decltype(${container.name}[0]->${variable.name})>::type::value_type"
!isInBank && isMultiport -> "std::remove_reference<decltype($name[0])>::type::value_type"
else -> "std::remove_reference<decltype($name)>::type::value_type"
}
}

private fun generateDeclaration(port: Port): String = with(port) {
return if (isMultiport) {
//val initializerLists = (0 until getValidWidth()).joinToString(", ") { """{"${name}_$it", this}""" }
Expand Down Expand Up @@ -84,7 +102,7 @@ class CppPortGenerator(private val reactor: Reactor) {
${name}.reserve($width);
for (size_t __lf_idx = 0; __lf_idx < $width; __lf_idx++) {
std::string __lf_port_name = "${name}_" + std::to_string(__lf_idx);
${name}.emplace_back(__lf_port_name, this, (reactor::BaseMultiport*)&${name}, __lf_idx);
${name}.emplace_back(__lf_port_name, this);
}
""".trimIndent()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ package org.lflang.generator.cpp

import org.lflang.ErrorReporter
import org.lflang.generator.PrependOperator
import org.lflang.generator.cpp.CppParameterGenerator.Companion.targetType
import org.lflang.generator.cpp.CppParameterGenerator.Companion.typeAlias
import org.lflang.isGeneric
import org.lflang.lf.Reactor
import org.lflang.toText
Expand Down Expand Up @@ -58,6 +56,7 @@ class CppReactorGenerator(private val reactor: Reactor, fileConfig: CppFileConfi
private val ports = CppPortGenerator(reactor)
private val reactions = CppReactionGenerator(reactor, ports, instances)
private val assemble = CppAssembleMethodGenerator(reactor)
private val connections = CppConnectionGenerator(reactor)

private fun publicPreamble() =
reactor.preambles.filter { it.isPublic }
Expand Down Expand Up @@ -113,6 +112,7 @@ class CppReactorGenerator(private val reactor: Reactor, fileConfig: CppFileConfi
${" | "..instances.generateDeclarations()}
${" | "..timers.generateDeclarations()}
${" | "..actions.generateDeclarations()}
${" | "..connections.generateDeclarations()}
${" | "..reactions.generateReactionViews()}
${" | "..reactions.generateDeclarations()}
|
Expand Down Expand Up @@ -184,6 +184,7 @@ class CppReactorGenerator(private val reactor: Reactor, fileConfig: CppFileConfi
${" | "..instances.generateInitializers()}
${" | "..timers.generateInitializers()}
${" | "..actions.generateInitializers()}
${" | "..connections.generateInitializers()}
${" | "..reactions.generateReactionViewInitializers()}
|{
${" | "..ports.generateConstructorInitializers()}
Expand Down
2 changes: 1 addition & 1 deletion test/C/src/PhysicalConnection.lf
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ reactor Destination {
input in: int

reaction(in) {=
interval_t time = lf_time_physical_elapsed();
interval_t time = lf_time_logical_elapsed();
printf("Received %d at logical time %lld.\n", in->value, time);
if (time <= 0LL) {
fprintf(stderr, "ERROR: Logical time should have been greater than zero.\n");
Expand Down
Loading