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

Modal multiport #2422

Merged
merged 14 commits into from
Oct 17, 2024
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
Loading
Loading