Skip to content

Commit

Permalink
Merge pull request #2422 from lf-lang/modal-multiport
Browse files Browse the repository at this point in the history
Modal multiport
  • Loading branch information
edwardalee authored Oct 16, 2024
2 parents b76c063 + 9aea679 commit 4d24766
Show file tree
Hide file tree
Showing 11 changed files with 444 additions and 45 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/org/lflang/ast/ASTUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ public static ReactorInstance createMainReactorInstance(
messageReporter
.nowhere()
.error("Main reactor has causality cycles. Skipping code generation.");
return null;
return main; // Avoid NPE.
}
// Inform the run-time of the breadth/parallelism of the reaction graph
var breadth = reactionInstanceGraph.getBreadth();
Expand Down
11 changes: 3 additions & 8 deletions core/src/main/java/org/lflang/generator/GeneratorBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import org.lflang.lf.Mode;
import org.lflang.lf.Reaction;
import org.lflang.lf.Reactor;
import org.lflang.lf.VarRef;
import org.lflang.target.Target;
import org.lflang.target.TargetConfig;
import org.lflang.target.property.FilesProperty;
Expand Down Expand Up @@ -441,13 +442,7 @@ private void transformConflictingConnectionsInModalReactors(Set<Resource> resour
reaction.getEffects().add(destRef);

var code = factory.createCode();
var source =
(sourceRef.getContainer() != null ? sourceRef.getContainer().getName() + "." : "")
+ sourceRef.getVariable().getName();
var dest =
(destRef.getContainer() != null ? destRef.getContainer().getName() + "." : "")
+ destRef.getVariable().getName();
code.setBody(getConflictingConnectionsInModalReactorsBody(source, dest));
code.setBody(getConflictingConnectionsInModalReactorsBody(sourceRef, destRef));
reaction.setCode(code);

EcoreUtil.remove(connection);
Expand All @@ -464,7 +459,7 @@ private void transformConflictingConnectionsInModalReactors(Set<Resource> resour
* <p>This method needs to be overridden in target specific code generators that support modal
* reactors.
*/
protected String getConflictingConnectionsInModalReactorsBody(String source, String dest) {
protected String getConflictingConnectionsInModalReactorsBody(VarRef source, VarRef dest) {
messageReporter
.nowhere()
.error(
Expand Down
97 changes: 88 additions & 9 deletions core/src/main/java/org/lflang/generator/c/CGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@
import org.lflang.lf.Reactor;
import org.lflang.lf.ReactorDecl;
import org.lflang.lf.StateVar;
import org.lflang.lf.VarRef;
import org.lflang.lf.Variable;
import org.lflang.lf.WidthSpec;
import org.lflang.target.Target;
import org.lflang.target.TargetConfig;
import org.lflang.target.property.BuildCommandsProperty;
Expand Down Expand Up @@ -408,7 +410,10 @@ public void doGenerate(Resource resource, LFGeneratorContext context) {
if (!isOSCompatible()) return; // Incompatible OS and configuration

// Perform set up that does not generate code
setUpGeneralParameters();
if (!setUpGeneralParameters()) {
// Failure.
return;
}

FileUtil.createDirectoryIfDoesNotExist(fileConfig.getSrcGenPath().toFile());
FileUtil.createDirectoryIfDoesNotExist(fileConfig.binPath.toFile());
Expand Down Expand Up @@ -659,12 +664,81 @@ public void checkModalReactorSupport(boolean __) {
}

@Override
protected String getConflictingConnectionsInModalReactorsBody(String source, String dest) {
return String.join(
"\n",
"// Generated forwarding reaction for connections with the same destination",
"// but located in mutually exclusive modes.",
"lf_set(" + dest + ", " + source + "->value);");
protected String getConflictingConnectionsInModalReactorsBody(VarRef sourceRef, VarRef destRef) {
Instantiation sourceContainer = sourceRef.getContainer();
Instantiation destContainer = destRef.getContainer();
Port sourceAsPort = (Port) sourceRef.getVariable();
Port destAsPort = (Port) destRef.getVariable();
WidthSpec sourceWidth = sourceAsPort.getWidthSpec();
WidthSpec destWidth = destAsPort.getWidthSpec();

// NOTE: Have to be careful with naming count variables because if the name matches
// that of a port, the program will fail to compile.

// If the source or dest is a port of a bank, we need to iterate over it.
var isBank = false;
Instantiation bank = null;
var sourceContainerRef = "";
if (sourceContainer != null) {
sourceContainerRef = sourceContainer.getName() + ".";
bank = sourceContainer;
if (bank.getWidthSpec() != null) {
isBank = true;
sourceContainerRef = sourceContainer.getName() + "[_lf_j].";
}
}
var sourceIndex = isBank ? "_lf_i" : "_lf_c";
var source =
sourceContainerRef
+ sourceAsPort.getName()
+ ((sourceWidth != null) ? "[" + sourceIndex + "]" : "");
var destContainerRef = "";
var destIndex = "_lf_c";
if (destContainer != null) {
destIndex = "_lf_i";
destContainerRef = destContainer.getName() + ".";
if (bank == null) {
bank = destContainer;
if (bank.getWidthSpec() != null) {
isBank = true;
destContainerRef = destContainer.getName() + "[_lf_j].";
}
}
}
var dest =
destContainerRef
+ destAsPort.getName()
+ ((destWidth != null) ? "[" + destIndex + "]" : "");
var result = new StringBuilder();
result.append("{ int _lf_c = 0; SUPPRESS_UNUSED_WARNING(_lf_c); ");
// If either side is a bank (only one side should be), iterate over it.
if (isBank) {
var width = new StringBuilder();
for (var term : bank.getWidthSpec().getTerms()) {
if (!width.isEmpty()) width.append(" + ");
if (term.getCode() != null) width.append(term.getCode().getBody());
else if (term.getParameter() != null)
width.append("self->" + term.getParameter().getName());
else width.append(term.getWidth());
}
result.append("for(int _lf_j = 0; _lf_j < " + width.toString() + "; _lf_j++) { ");
}
// If either side is a multiport, iterate.
// Note that one side could be a multiport of width 1 and the other an ordinary port.
if (sourceWidth != null || destWidth != null) {
var width =
(sourceAsPort.getWidthSpec() != null)
? sourceContainerRef + sourceAsPort.getName()
: destContainerRef + destAsPort.getName();
result.append("for(int _lf_i = 0; _lf_i < " + width + "_width; _lf_i++) { ");
}
result.append("lf_set(" + dest + ", " + source + "->value); _lf_c++; ");
if (sourceWidth != null || destAsPort.getWidthSpec() != null) {
result.append(" }");
}
if (isBank) result.append(" }");
result.append(" }");
return result.toString();
}

/** Set the scheduler type in the target config as needed. */
Expand Down Expand Up @@ -1939,8 +2013,8 @@ protected DockerGenerator getDockerGenerator(LFGeneratorContext context) {
// //////////////////////////////////////////
// // Protected methods.

// Perform set up that does not generate code
protected void setUpGeneralParameters() {
// Perform set up that does not generate code. Return false on failure.
protected boolean setUpGeneralParameters() {
accommodatePhysicalActionsIfPresent();
CompileDefinitionsProperty.INSTANCE.update(
targetConfig,
Expand All @@ -1950,6 +2024,10 @@ protected void setUpGeneralParameters() {
// Create the main reactor instance if there is a main reactor.
this.main =
ASTUtils.createMainReactorInstance(mainDef, reactors, messageReporter, targetConfig);
if (this.main == null) {
// Something went wrong (causality cycle?). Stop.
return false;
}
if (hasModalReactors) {
// So that each separate compile knows about modal reactors, do this:
CompileDefinitionsProperty.INSTANCE.update(targetConfig, Map.of("MODAL_REACTORS", "TRUE"));
Expand Down Expand Up @@ -2002,6 +2080,7 @@ protected void setUpGeneralParameters() {
}
pickCompilePlatform();
}
return true;
}

protected void handleProtoFiles() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,10 @@ public static int maxContainedReactorBankWidth(
nestedBreadcrumbs.add(mainDef);
}
int result = max;
Reactor parent = (Reactor) containedReactor.eContainer();
Reactor parent =
containedReactor.eContainer() instanceof Mode
? (Reactor) containedReactor.eContainer().eContainer()
: (Reactor) containedReactor.eContainer();
if (parent == ASTUtils.toDefinition(mainDef.getReactorClass())) {
// The parent is main, so there can't be any other instantiations of it.
return ASTUtils.width(containedReactor.getWidthSpec(), null);
Expand Down
112 changes: 87 additions & 25 deletions core/src/main/java/org/lflang/generator/python/PythonGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,19 @@
import org.lflang.generator.docker.PythonDockerGenerator;
import org.lflang.lf.Action;
import org.lflang.lf.Input;
import org.lflang.lf.Instantiation;
import org.lflang.lf.Model;
import org.lflang.lf.Output;
import org.lflang.lf.Port;
import org.lflang.lf.Reaction;
import org.lflang.lf.Reactor;
import org.lflang.lf.VarRef;
import org.lflang.lf.WidthSpec;
import org.lflang.target.Target;
import org.lflang.target.property.DockerProperty;
import org.lflang.target.property.ProtobufsProperty;
import org.lflang.util.FileUtil;
import org.lflang.util.LFCommand;
import org.lflang.util.StringUtil;

/**
* Generator for Python target. This class generates Python code defining each reactor class given
Expand Down Expand Up @@ -539,22 +541,94 @@ protected void generateSelfStructExtension(
}

@Override
protected String getConflictingConnectionsInModalReactorsBody(String source, String dest) {
// NOTE: Strangely, a newline is needed at the beginning or indentation
// gets swallowed.
return String.join(
"\n",
"\n# Generated forwarding reaction for connections with the same destination",
"# but located in mutually exclusive modes.",
dest + ".set(" + source + ".value)\n");
protected String getConflictingConnectionsInModalReactorsBody(VarRef sourceRef, VarRef destRef) {
Instantiation sourceContainer = sourceRef.getContainer();
Instantiation destContainer = destRef.getContainer();
Port sourceAsPort = (Port) sourceRef.getVariable();
Port destAsPort = (Port) destRef.getVariable();
WidthSpec sourceWidth = sourceAsPort.getWidthSpec();
WidthSpec destWidth = destAsPort.getWidthSpec();

// NOTE: Have to be careful with naming count variables because if the name matches
// that of a port, the program will fail to compile.

// If the source or dest is a port of a bank, we need to iterate over it.
var isBank = false;
Instantiation bank = null;
var sourceContainerRef = "";
if (sourceContainer != null) {
sourceContainerRef = sourceContainer.getName() + ".";
bank = sourceContainer;
if (bank.getWidthSpec() != null) {
isBank = true;
sourceContainerRef = sourceContainer.getName() + "[_lf_j].";
}
}
var sourceIndex = isBank ? "_lf_i" : "_lf_c";
var source =
sourceContainerRef
+ sourceAsPort.getName()
+ ((sourceWidth != null) ? "[" + sourceIndex + "]" : "");
var destContainerRef = "";
var destIndex = "_lf_c";
if (destContainer != null) {
destIndex = "_lf_i";
destContainerRef = destContainer.getName() + ".";
if (bank == null) {
bank = destContainer;
if (bank.getWidthSpec() != null) {
isBank = true;
destContainerRef = destContainer.getName() + "[_lf_j].";
}
}
}
var dest =
destContainerRef
+ destAsPort.getName()
+ ((destWidth != null) ? "[" + destIndex + "]" : "");
var result = new CodeBuilder();
// If either side is a bank (only one side should be), iterate over it.
result.pr("_lf_c = 0"); // Counter variable over nested loop if there is a bank and multiport.
if (isBank) {
var width = new StringBuilder();
for (var term : bank.getWidthSpec().getTerms()) {
if (!width.isEmpty()) width.append(" + ");
if (term.getCode() != null) width.append(term.getCode().getBody());
else if (term.getParameter() != null) width.append("self." + term.getParameter().getName());
else width.append(term.getWidth());
}
result.pr("for _lf_j in range(" + width + "):");
result.indent();
}
// If either side is a multiport, iterate.
// Note that one side could be a multiport of width 1 and the other an ordinary port.
if (sourceWidth != null || destWidth != null) {
var width =
(sourceAsPort.getWidthSpec() != null)
? sourceContainerRef + sourceAsPort.getName()
: destContainerRef + destAsPort.getName();
result.pr("for _lf_i in range(" + width + ".width):");
result.indent();
}
result.pr(dest + ".set(" + source + ".value)");
result.pr("_lf_c += 1"); // Increment the count.
result.unindent();
if (isBank) {
result.unindent();
}
return result.toString();
}

@Override
protected void setUpGeneralParameters() {
super.setUpGeneralParameters();
if (hasModalReactors) {
targetConfig.compileAdditionalSources.add("lib/modal_models/impl.c");
protected boolean setUpGeneralParameters() {
boolean result = super.setUpGeneralParameters();
if (result) {
if (hasModalReactors) {
targetConfig.compileAdditionalSources.add("lib/modal_models/impl.c");
}
return true;
}
return false;
}

@Override
Expand Down Expand Up @@ -622,18 +696,6 @@ private static String generateCmakeInstall(FileConfig fileConfig) {
.replace("<pyMainName>", pyMainName);
}

/**
* Generate a ({@code key}, {@code val}) tuple pair for the {@code define_macros} field of the
* Extension class constructor from setuptools.
*
* @param key The key of the macro entry
* @param val The value of the macro entry
* @return A ({@code key}, {@code val}) tuple pair as String
*/
private static String generateMacroEntry(String key, String val) {
return "(" + StringUtil.addDoubleQuotes(key) + ", " + StringUtil.addDoubleQuotes(val) + ")";
}

/**
* Generate the name of the python module.
*
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/resources/lib/c/reactor-c
Loading

0 comments on commit 4d24766

Please sign in to comment.