use of org.lflang.lf.Instantiation in project lingua-franca by lf-lang.
the class GeneratorBase method analyzeFederates.
/**
* Analyze the AST to determine whether code is being mapped to
* single or to multiple target machines. If it is being mapped
* to multiple machines, then set the {@link #isFederated} field to true,
* create a FederateInstance for each federate, and record various
* properties of the federation
*
* In addition, for each top-level connection, add top-level reactions to the AST
* that send and receive messages over the network.
*
* This class is target independent, so the target code
* generator still has quite a bit of work to do.
* It needs to provide the body of the sending and
* receiving reactions. It also needs to provide the
* runtime infrastructure that uses the dependency
* information between federates. See the C target
* for a reference implementation.
*/
private void analyzeFederates(LFGeneratorContext context) {
// Next, if there actually are federates, analyze the topology
// interconnecting them and replace the connections between them
// with an action and two reactions.
Reactor mainReactor = mainDef != null ? ASTUtils.toDefinition(mainDef.getReactorClass()) : null;
if (mainDef == null || !mainReactor.isFederated()) {
// The program is not federated.
// Ensure federates is never empty.
FederateInstance federateInstance = new FederateInstance(null, 0, 0, this, errorReporter);
federates.add(federateInstance);
federateByID.put(0, federateInstance);
} else {
// The Lingua Franca program is federated
isFederated = true;
// If the "--rti" flag is given to the compiler, use the argument from the flag.
if (context.getArgs().containsKey("rti")) {
setFederationRTIProperties(context);
} else if (mainReactor.getHost() != null) {
// If not specified, this defaults to 'localhost'
if (mainReactor.getHost().getAddr() != null) {
federationRTIProperties.put("host", mainReactor.getHost().getAddr());
}
// If not specified, this defaults to 14045
if (mainReactor.getHost().getPort() != 0) {
federationRTIProperties.put("port", mainReactor.getHost().getPort());
}
// Get the user information, if specified.
if (mainReactor.getHost().getUser() != null) {
federationRTIProperties.put("user", mainReactor.getHost().getUser());
}
}
// Since federates are always within the main (federated) reactor,
// create a list containing just that one containing instantiation.
// This will be used to look up parameter values.
List<Instantiation> mainReactorContext = new ArrayList<>();
mainReactorContext.add(mainDef);
// Create a FederateInstance for each top-level reactor.
for (Instantiation instantiation : ASTUtils.allInstantiations(mainReactor)) {
int bankWidth = ASTUtils.width(instantiation.getWidthSpec(), mainReactorContext);
if (bankWidth < 0) {
errorReporter.reportError(instantiation, "Cannot determine bank width! Assuming width of 1.");
// Continue with a bank width of 1.
bankWidth = 1;
}
// Create one federate instance for each instance in a bank of reactors.
List<FederateInstance> federateInstances = new ArrayList<>(bankWidth);
for (int i = 0; i < bankWidth; i++) {
// Assign an integer ID to the federate.
int federateID = federates.size();
FederateInstance federateInstance = new FederateInstance(instantiation, federateID, i, this, errorReporter);
federateInstance.bankIndex = i;
federates.add(federateInstance);
federateInstances.add(federateInstance);
federateByID.put(federateID, federateInstance);
if (instantiation.getHost() != null) {
federateInstance.host = instantiation.getHost().getAddr();
// The following could be 0.
federateInstance.port = instantiation.getHost().getPort();
// The following could be null.
federateInstance.user = instantiation.getHost().getUser();
/* FIXME: The at keyword should support a directory component.
* federateInstance.dir = instantiation.getHost().dir
*/
if (federateInstance.host != null && federateInstance.host != "localhost" && federateInstance.host != "0.0.0.0") {
federateInstance.isRemote = true;
}
}
}
if (federatesByInstantiation == null) {
federatesByInstantiation = new LinkedHashMap<Instantiation, List<FederateInstance>>();
}
federatesByInstantiation.put(instantiation, federateInstances);
}
// In a federated execution, we need keepalive to be true,
// otherwise a federate could exit simply because it hasn't received
// any messages.
targetConfig.keepalive = true;
// Analyze the connection topology of federates.
// First, find all the connections between federates.
// For each connection between federates, replace it in the
// AST with an action (which inherits the delay) and two reactions.
// The action will be physical for physical connections and logical
// for logical connections.
replaceFederateConnectionsWithActions();
// Remove the connections at the top level
mainReactor.getConnections().clear();
}
}
use of org.lflang.lf.Instantiation in project lingua-franca by lf-lang.
the class GeneratorBase method doGenerate.
/**
* Generate code from the Lingua Franca model contained by the specified resource.
*
* This is the main entry point for code generation. This base class finds all
* reactor class definitions, including any reactors defined in imported .lf files
* (except any main reactors in those imported files), and adds them to the
* {@link #GeneratorBase.reactors reactors} list. If errors occur during
* generation, then a subsequent call to errorsOccurred() will return true.
* @param resource The resource containing the source code.
* @param context Context relating to invocation of the code generator.
* In stand alone mode, this object is also used to relay CLI arguments.
*/
public void doGenerate(Resource resource, LFGeneratorContext context) {
GeneratorUtils.setTargetConfig(context, GeneratorUtils.findTarget(fileConfig.resource), targetConfig, errorReporter);
cleanIfNeeded(context);
printInfo(context.getMode());
// Markers mark problems in the Eclipse IDE when running in integrated mode.
if (errorReporter instanceof EclipseErrorReporter) {
((EclipseErrorReporter) errorReporter).clearMarkers();
}
ASTUtils.setMainName(fileConfig.resource, fileConfig.name);
createMainInstantiation();
// Check if there are any conflicting main reactors elsewhere in the package.
if (Objects.equal(context.getMode(), LFGeneratorContext.Mode.STANDALONE) && mainDef != null) {
for (String conflict : new MainConflictChecker(fileConfig).conflicts) {
errorReporter.reportError(this.mainDef.getReactorClass(), "Conflicting main reactor in " + conflict);
}
}
// Configure the command factory
commandFactory.setVerbose();
if (Objects.equal(context.getMode(), LFGeneratorContext.Mode.STANDALONE) && context.getArgs().containsKey("quiet")) {
commandFactory.setQuiet();
}
// This must be done before desugaring delays below.
analyzeFederates(context);
// Process target files. Copy each of them into the src-gen dir.
// FIXME: Should we do this here? This doesn't make sense for federates the way it is
// done here.
copyUserFiles(this.targetConfig, this.fileConfig);
// Collect reactors and create an instantiation graph.
// These are needed to figure out which resources we need
// to validate, which happens in setResources().
setReactorsAndInstantiationGraph(context.getMode());
GeneratorUtils.validate(context, fileConfig, instantiationGraph, errorReporter);
List<Resource> allResources = GeneratorUtils.getResources(reactors);
resources.addAll(// FIXME: This filter reproduces the behavior of the method it replaces. But why must it be so complicated? Why are we worried about weird corner cases like this?
allResources.stream().filter(it -> !Objects.equal(it, fileConfig.resource) || mainDef != null && it == mainDef.getReactorClass().eResource()).map(it -> GeneratorUtils.getLFResource(it, fileConfig.getSrcGenBasePath(), context, errorReporter)).collect(Collectors.toList()));
GeneratorUtils.accommodatePhysicalActionsIfPresent(allResources, getTarget().setsKeepAliveOptionAutomatically(), targetConfig, errorReporter);
// FIXME: Should the GeneratorBase pull in `files` from imported
// resources?
// Reroute connections that have delays associated with them via
// generated delay reactors.
transformDelays();
// Transform connections that reside in mutually exclusive modes and are otherwise conflicting
// This should be done before creating the instantiation graph
transformConflictingConnectionsInModalReactors();
// Invoke these functions a second time because transformations
// may have introduced new reactors!
setReactorsAndInstantiationGraph(context.getMode());
// Check for existence and support of modes
hasModalReactors = IterableExtensions.exists(reactors, it -> !it.getModes().isEmpty());
checkModalReactorSupport(false);
enableSupportForSerializationIfApplicable(context.getCancelIndicator());
}
use of org.lflang.lf.Instantiation in project lingua-franca by lf-lang.
the class FedASTUtils method findMaxSTP.
/**
* Find the maximum STP offset for the given 'port'.
*
* An STP offset predicate can be nested in contained reactors in
* the federate.
* @param port The port to generate the STP list for.
* @param generator The GeneratorBase instance used to perform some target-specific actions
* @param reactor The top-level reactor (not the federate reactor)
* @return The maximum STP as a TimeValue
*/
private static TimeValue findMaxSTP(Variable port, FederateInstance instance, GeneratorBase generator, Reactor reactor) {
// Find a list of STP offsets (if any exists)
List<Value> STPList = new LinkedList<>();
// First, check if there are any connections to contained reactors that
// need to be handled
List<Connection> connectionsWithPort = ASTUtils.allConnections(reactor).stream().filter(c -> c.getLeftPorts().stream().anyMatch((VarRef v) -> v.getVariable().equals(port))).collect(Collectors.toList());
// Find the list of reactions that have the port as trigger or source
// (could be a variable name)
List<Reaction> reactionsWithPort = ASTUtils.allReactions(reactor).stream().filter(r -> {
// Check the triggers of reaction r first
return r.getTriggers().stream().anyMatch(t -> {
if (t instanceof VarRef) {
// Check if the variables match
return ((VarRef) t).getVariable() == port;
} else {
// Not a network port (startup or shutdown)
return false;
}
}) || // Then check the sources of reaction r
r.getSources().stream().anyMatch(s -> s.getVariable() == port);
}).collect(Collectors.toList());
// Find a list of STP offsets (if any exists)
if (generator.isFederatedAndDecentralized()) {
for (Reaction r : safe(reactionsWithPort)) {
if (!instance.contains(r)) {
continue;
}
// If not, assume it is zero
if (r.getStp() != null) {
if (r.getStp().getValue().getParameter() != null) {
List<Instantiation> instantList = new ArrayList<>();
instantList.add(instance.instantiation);
STPList.addAll(ASTUtils.initialValue(r.getStp().getValue().getParameter(), instantList));
} else {
STPList.add(r.getStp().getValue());
}
}
}
// Check the children for STPs as well
for (Connection c : safe(connectionsWithPort)) {
VarRef childPort = c.getRightPorts().get(0);
Reactor childReactor = (Reactor) childPort.getVariable().eContainer();
// Find the list of reactions that have the port as trigger or
// source (could be a variable name)
List<Reaction> childReactionsWithPort = ASTUtils.allReactions(childReactor).stream().filter(r -> r.getTriggers().stream().anyMatch(t -> {
if (t instanceof VarRef) {
// Check if the variables match
return ((VarRef) t).getVariable() == childPort.getVariable();
} else {
// Not a network port (startup or shutdown)
return false;
}
}) || r.getSources().stream().anyMatch(s -> s.getVariable() == childPort.getVariable())).collect(Collectors.toList());
for (Reaction r : safe(childReactionsWithPort)) {
if (!instance.contains(r)) {
continue;
}
// If not, assume it is zero
if (r.getStp() != null) {
if (r.getStp().getValue() instanceof Parameter) {
List<Instantiation> instantList = new ArrayList<>();
instantList.add(childPort.getContainer());
STPList.addAll(ASTUtils.initialValue(r.getStp().getValue().getParameter(), instantList));
} else {
STPList.add(r.getStp().getValue());
}
}
}
}
}
return STPList.stream().map(ASTUtils::getLiteralTimeValue).filter(Objects::nonNull).reduce(TimeValue.ZERO, TimeValue::max);
}
use of org.lflang.lf.Instantiation in project lingua-franca by lf-lang.
the class ModelInfo method detectOverflow.
/**
* Given a parameter that is used in a deadline specification, recursively
* track down its definition and check whether it is overflowing. Also
* detect and report overrides that are overflowing.
*/
private boolean detectOverflow(Set<Instantiation> visited, Parameter current) {
var overflow = false;
// Determine whether the parameter's default value overflows or not.
if (isTooLarge(ASTUtils.getDefaultAsTimeValue(current))) {
this.overflowingParameters.add(current);
overflow = true;
}
// Iterate over the instantiations of the reactor in which the
// current parameter was found.
Set<Instantiation> instantiations = this.instantiationGraph.getInstantiations((Reactor) current.eContainer());
for (var instantiation : instantiations) {
// Only visit each instantiation once per deadline to avoid cycles.
if (!visited.contains(instantiation)) {
visited.add(instantiation);
// Find assignments that override the current parameter.
for (var assignment : instantiation.getParameters()) {
if (assignment.getLhs().equals(current)) {
Parameter parameter = assignment.getRhs().get(0).getParameter();
if (parameter != null) {
// Check for overflow in the referenced parameter.
overflow = detectOverflow(visited, parameter) || overflow;
} else {
// constant; check whether it is too large.
if (isTooLarge(ASTUtils.getLiteralTimeValue(assignment.getRhs().get(0)))) {
this.overflowingAssignments.add(assignment);
overflow = true;
}
}
}
}
}
}
return overflow;
}
use of org.lflang.lf.Instantiation in project lingua-franca by lf-lang.
the class ASTUtils method inferPortWidth.
/**
* Infer the width of a port reference in a connection.
* The port reference one or two parts, a port and an (optional) container
* which is an Instantiation that may refer to a bank of reactors.
* The width will be the product of the bank width and the port width.
* The returned value will be 1 if the port is not in a bank and is not a multiport.
*
* If the width cannot be determined, this will return -1.
* The width cannot be determined if the list of instantiations is
* missing or incomplete.
*
* The instantiations list is as in
* {@link initialValue(Parameter, List<Instantiation>}.
* The first element on this list should be the instantiation
* that contains the specified connection.
*
* @param reference A port reference.
* @param connection A connection, or null if not in the context of a connection.
* @param instantiations The (optional) list of instantiations.
*
* @return The width or -1 if it could not be determined.
*
* @throws IllegalArgumentException If an instantiation provided is not as
* given above or if the chain of instantiations is not nested.
*/
public static int inferPortWidth(VarRef reference, Connection connection, List<Instantiation> instantiations) {
if (reference.getVariable() instanceof Port) {
// If the port is given as a.b, then we want to prepend a to
// the list of instantiations to determine the width of this port.
List<Instantiation> extended = instantiations;
if (reference.getContainer() != null) {
extended = new ArrayList<>();
extended.add(reference.getContainer());
if (instantiations != null) {
extended.addAll(instantiations);
}
}
int portWidth = width(((Port) reference.getVariable()).getWidthSpec(), extended);
if (portWidth < 0) {
// Could not determine port width.
return -1;
}
// Next determine the bank width. This may be unspecified, in which
// case it has to be inferred using the connection.
int bankWidth = 1;
if (reference.getContainer() != null) {
bankWidth = width(reference.getContainer().getWidthSpec(), instantiations);
if (bankWidth < 0 && connection != null) {
// Try to infer the bank width from the connection.
if (reference.getContainer().getWidthSpec().isOfVariableLength()) {
// This occurs for a bank of delays.
int leftWidth = 0;
int rightWidth = 0;
int leftOrRight = 0;
for (VarRef leftPort : connection.getLeftPorts()) {
if (leftPort == reference) {
if (leftOrRight != 0) {
throw new InvalidSourceException("Multiple ports with variable width on a connection.");
}
// Indicate that this port is on the left.
leftOrRight = -1;
} else {
// The left port is not the same as this reference.
int otherWidth = inferPortWidth(leftPort, connection, instantiations);
if (otherWidth < 0) {
// Cannot determine width.
return -1;
}
leftWidth += otherWidth;
}
}
for (VarRef rightPort : connection.getRightPorts()) {
if (rightPort == reference) {
if (leftOrRight != 0) {
throw new InvalidSourceException("Multiple ports with variable width on a connection.");
}
// Indicate that this port is on the right.
leftOrRight = 1;
} else {
int otherWidth = inferPortWidth(rightPort, connection, instantiations);
if (otherWidth < 0) {
// Cannot determine width.
return -1;
}
rightWidth += otherWidth;
}
}
int discrepancy = 0;
if (leftOrRight < 0) {
// This port is on the left.
discrepancy = rightWidth - leftWidth;
} else if (leftOrRight > 0) {
// This port is on the right.
discrepancy = leftWidth - rightWidth;
}
// Check that portWidth divides the discrepancy.
if (discrepancy % portWidth != 0) {
// This is an error.
return -1;
}
bankWidth = discrepancy / portWidth;
} else {
// Could not determine the bank width.
return -1;
}
}
}
return portWidth * bankWidth;
}
// Argument is not a port.
return -1;
}
Aggregations