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

Multiport iterator for more efficient access to sparse inputs #1298

Merged
merged 33 commits into from
Jul 23, 2022
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
257438c
Put is_present as the first element of port structs
edwardalee Jul 4, 2022
13e1e4e
First attempt at an input iterator
edwardalee Jul 4, 2022
bbf3dd1
Complete template for sparse optimization.
edwardalee Jul 5, 2022
e72bbb5
Added struct to support sparse inputs.
edwardalee Jul 9, 2022
2975669
Align with reactor-c
edwardalee Jul 9, 2022
225c99c
Merge branch 'master' into input-iterator
edwardalee Jul 13, 2022
03bbd48
Got sparsity working for first time only. Need to reset.
edwardalee Jul 13, 2022
51554d4
Reset sparse record between iterations
edwardalee Jul 16, 2022
3158930
Create iterator struct and use it
edwardalee Jul 17, 2022
736f333
Bug fixes with fallback strategy for sparse I/O.
edwardalee Jul 17, 2022
607edd5
Enabled @sparse annotation
edwardalee Jul 18, 2022
3a07c00
Added test
edwardalee Jul 18, 2022
25155ee
Correct multiple definition error
edwardalee Jul 19, 2022
6faa5d4
Update SET_ARRAY for CCpp
edwardalee Jul 19, 2022
d3df68b
Made port struct conform with changes in reactor-c.
edwardalee Jul 19, 2022
7551f8a
Made port struct conform with changes in reactor-c.
edwardalee Jul 19, 2022
14c6a18
Merge branch 'master' into input-iterator
edwardalee Jul 19, 2022
9cab6cd
Changed iterator API per conversation with @lhstrh.
edwardalee Jul 21, 2022
794f4c4
Try to fix build issue.
petervdonovan Jul 22, 2022
351f3f4
Align with passing reactor-c branch
edwardalee Jul 22, 2022
4e89c5f
Update test/C/src/multiport/Sparse.lf
edwardalee Jul 23, 2022
6471525
Update test/C/src/multiport/SparseFallback.lf
edwardalee Jul 23, 2022
7a6b170
Bump Klighd to version 2.2
a-sr Jul 15, 2022
9fd5e15
diagrams: Added blacklist of internal synthesis properties
a-sr Jul 15, 2022
9043785
Fixed typo
a-sr Jul 19, 2022
ad44416
modes: Fixed ordering of reaction if placed in between or atfer modes.
a-sr Jul 21, 2022
d0d4db6
modes: Improved comment in test model
a-sr Jul 21, 2022
a739ede
Update CHANGELOG.md
francabot Jul 22, 2022
69aed78
Bump version to 0.3.0
francabot Jul 22, 2022
567ba70
Bump version to 0.3.1-SNAPSHOT
francabot Jul 22, 2022
c4cd88c
Update build.yml
lhstrh Jul 22, 2022
8f5a9d1
Make sparse annotation case-insensitive
edwardalee Jul 23, 2022
9ad90a2
Align with main branch of reactor-c and reactor-c-py
edwardalee Jul 23, 2022
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
2 changes: 1 addition & 1 deletion org.lflang/src/lib/py/reactor-c-py
1 change: 0 additions & 1 deletion org.lflang/src/org/lflang/ASTUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
import org.lflang.lf.Action;
import org.lflang.lf.ActionOrigin;
import org.lflang.lf.Assignment;
import org.lflang.lf.Attribute;
import org.lflang.lf.Code;
import org.lflang.lf.Connection;
import org.lflang.lf.Element;
Expand Down
15 changes: 14 additions & 1 deletion org.lflang/src/org/lflang/AttributeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,20 @@ public static String findLabelAttribute(EObject node) {
.map(it -> it.getAttrParms().get(0).getValue().getStr())
.findFirst()
.orElse(null);

}

/**
* Return true if the specified node is an Input and has an {@code @sparse}
* attribute.
* @param node An AST node.
*/
public static boolean isSparse(EObject node) {
if (node instanceof Input) {
for (var attribute : getAttributes(node)) {
if (attribute.getAttrName().equals("sparse")) return true;
edwardalee marked this conversation as resolved.
Show resolved Hide resolved
lhstrh marked this conversation as resolved.
Show resolved Hide resolved
}
}
return false;
}

/**
Expand Down
2 changes: 2 additions & 0 deletions org.lflang/src/org/lflang/generator/c/CCoreFilesUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ private static List<String> getBaseCoreFiles() {
"tag.c",
"trace.h",
"trace.c",
"port.h",
"port.c",
"utils/pqueue.c",
"utils/pqueue.h",
"utils/pqueue_support.h",
Expand Down
1 change: 1 addition & 0 deletions org.lflang/src/org/lflang/generator/c/CGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -2224,6 +2224,7 @@ protected void setUpGeneralParameters() {
targetConfig.compileDefinitions.put("LOG_LEVEL", targetConfig.logLevel.ordinal() + "");
targetConfig.compileAdditionalSources.addAll(CCoreFilesUtils.getCTargetSrc());
targetConfig.compileAdditionalSources.add("core" + File.separator + "mixed_radix.c");
targetConfig.compileAdditionalSources.add("core" + File.separator + "port.c");
setCSpecificDefaults();
// Create the main reactor instance if there is a main reactor.
createMainReactorInstance();
Expand Down
59 changes: 43 additions & 16 deletions org.lflang/src/org/lflang/generator/c/CPortGenerator.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.lflang.generator.c;

import org.lflang.ASTUtils;
import org.lflang.AttributeUtils;
import org.lflang.ErrorReporter;
import org.lflang.Target;
import org.lflang.generator.CodeBuilder;
Expand Down Expand Up @@ -62,9 +63,16 @@ public static String generateAuxiliaryStruct(
var code = new CodeBuilder();
code.pr("typedef struct {");
code.indent();
// NOTE: The following fields are required to be the first ones so that
// pointer to this struct can be cast to a (lf_port_base_t*) to access
// these fields for any port.
code.pr(String.join("\n",
"bool is_present;",
"lf_sparse_io_record_t* sparse_record;",
"int destination_channel;"
));
code.pr(valueDeclaration(port, target, errorReporter, types));
code.pr(String.join("\n",
"bool is_present;",
"int num_destinations;",
"lf_token_t* token;",
"int length;",
Expand All @@ -88,22 +96,39 @@ public static String initializeInputMultiport(
) {
var portRefName = CUtil.portRefName(input);
// If the port is a multiport, create an array.
return input.isMultiport() ?
String.join("\n",
portRefName+"_width = "+input.getWidth()+";",
"// Allocate memory for multiport inputs.",
portRefName+" = ("+variableStructType(input)+"**)_lf_allocate(",
" "+input.getWidth()+", sizeof("+variableStructType(input)+"*),",
" &"+reactorSelfStruct+"->base.allocations); ",
"// Set inputs by default to an always absent default input.",
"for (int i = 0; i < "+input.getWidth()+"; i++) {",
" "+portRefName+"[i] = &"+reactorSelfStruct+"->_lf_default__"+input.getName()+";",
if (input.isMultiport()) {
String result = String.join("\n",
portRefName+"_width = "+input.getWidth()+";",
"// Allocate memory for multiport inputs.",
portRefName+" = ("+variableStructType(input)+"**)_lf_allocate(",
" "+input.getWidth()+", sizeof("+variableStructType(input)+"*),",
" &"+reactorSelfStruct+"->base.allocations); ",
"// Set inputs by default to an always absent default input.",
"for (int i = 0; i < "+input.getWidth()+"; i++) {",
" "+portRefName+"[i] = &"+reactorSelfStruct+"->_lf_default__"+input.getName()+";",
"}"
);
if (AttributeUtils.isSparse(input.getDefinition())) {
return String.join("\n", result,
"if ("+input.getWidth()+" >= LF_SPARSE_WIDTH_THRESHOLD) {",
" "+portRefName+"__sparse = _lf_allocate(1,",
" sizeof(lf_sparse_io_record_t) + sizeof(size_t) * "+input.getWidth()+"/LF_SPARSE_CAPACITY_DIVIDER,",
" &"+reactorSelfStruct+"->base.allocations);",
" "+portRefName+"__sparse->capacity = "+input.getWidth()+"/LF_SPARSE_CAPACITY_DIVIDER;",
" if (_lf_sparse_io_record_sizes.start == NULL) {",
" _lf_sparse_io_record_sizes = vector_new(1);",
" }",
" vector_push(&_lf_sparse_io_record_sizes, (void*)&"+portRefName+"__sparse->size);",
"}"
) :
String.join("\n",
"// width of -2 indicates that it is not a multiport.",
portRefName+"_width = -2;"
);
}
return result;
} else {
return String.join("\n",
"// width of -2 indicates that it is not a multiport.",
portRefName+"_width = -2;"
lhstrh marked this conversation as resolved.
Show resolved Hide resolved
);
}
}

/**
Expand Down Expand Up @@ -220,7 +245,9 @@ private static void generateInputDeclarations(
variableStructType(input, decl)+"** _lf_"+inputName+";",
"int _lf_"+inputName+"_width;",
"// Default input (in case it does not get connected)",
variableStructType(input, decl)+" _lf_default__"+inputName+";"
variableStructType(input, decl)+" _lf_default__"+inputName+";",
"// Struct to support efficiently reading sparse inputs.",
"lf_sparse_io_record_t* _lf_"+inputName+"__sparse;"
));
} else {
// input is not a multiport.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public static String generateIncludeStatements(
code.pr("#include \"core/trace.c\"");
}
code.pr("#include \"core/mixed_radix.h\"");
code.pr("#include \"core/port.h\"");
return code.toString();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.stream.Collectors;

import org.lflang.ASTUtils;
import org.lflang.AttributeUtils;
import org.lflang.TargetConfig;
import org.lflang.TargetProperty.CoordinationType;
import org.lflang.TargetProperty.LogLevel;
Expand Down Expand Up @@ -483,8 +484,12 @@ private static String connectPortToEventualDestinations(
// An output port of a contained reactor is triggering a reaction.
code.pr(CUtil.portRefNested(dst, dr, db, dc)+" = ("+destStructType+"*)&"+CUtil.portRef(src, sr, sb, sc)+";");
} else {
// An output port is triggering
// An output port is triggering an input port.
code.pr(CUtil.portRef(dst, dr, db, dc)+" = ("+destStructType+"*)&"+CUtil.portRef(src, sr, sb, sc)+";");
if (AttributeUtils.isSparse(dst.getDefinition())) {
code.pr(CUtil.portRef(dst, dr, db, dc)+"->sparse_record = "+CUtil.portRefName(dst, dr, db, dc)+"__sparse;");
code.pr(CUtil.portRef(dst, dr, db, dc)+"->destination_channel = "+dc+";");
}
}
code.endScopedRangeBlock(srcRange, dstRange, isFederated);
}
Expand Down
10 changes: 8 additions & 2 deletions org.lflang/src/org/lflang/generator/python/PythonGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,16 @@ private PythonGenerator(FileConfig fileConfig, ErrorReporter errorReporter, Pyth
* statically allocated arrays in Lingua Franca.
* This template is defined as
* typedef struct {
* PyObject* value;
* bool is_present;
* lf_sparse_io_record_t* sparse_record; // NULL if there is no sparse record.
* int destination_channel; // -1 if there is no destination.
* PyObject* value;
* int num_destinations;
* FEDERATED_CAPSULE_EXTENSION
* lf_token_t* token;
* int length;
* void (*destructor) (void* value);
* void* (*copy_constructor) (void* value);
* FEDERATED_GENERIC_EXTENSION
* } generic_port_instance_struct;
*
* @see reactor-c-py/lib/pythontarget.h
Expand Down
83 changes: 51 additions & 32 deletions org.lflang/src/org/lflang/validation/AttributeSpec.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*************
* Copyright (c) 2019-2020, The University of California at Berkeley.
* Copyright (c) 2019-2022, The University of California at Berkeley.

* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
Expand Down Expand Up @@ -37,7 +37,7 @@
import org.lflang.lf.LfPackage.Literals;

/**
* Specification of the structure of an annotation.
* Specification of the structure of an attribute annotation.
*
* @author{Clément Fournier, TU Dresden, INSA Rennes}
* @author{Shaokai Lin <shaokai@berkeley.edu>}
Expand All @@ -52,7 +52,11 @@ class AttributeSpec {
public static final Map<String, AttributeSpec> ATTRIBUTE_SPECS_BY_NAME = new HashMap<>();

public AttributeSpec(List<AttrParamSpec> params) {
paramSpecByName = params.stream().collect(Collectors.toMap(it -> it.name, it -> it));
if (params != null) {
paramSpecByName = params.stream().collect(Collectors.toMap(it -> it.name, it -> it));
} else {
paramSpecByName = null;
}
}

/**
Expand All @@ -61,11 +65,17 @@ public AttributeSpec(List<AttrParamSpec> params) {
*/
public void check(LFValidator validator, Attribute attr) {
Set<String> seen;
// Check to see if there is one or multiple parameters.
if (attr.getAttrParms().size() == 1 && attr.getAttrParms().get(0).getName() == null) {
// If there is just one parameter, it is required to be named "value".
if (attr.getAttrParms() != null
&& attr.getAttrParms().size() == 1
&& attr.getAttrParms().get(0).getName() == null) {
// If we are in this branch,
// then the user has provided @attr("value"),
// which is a shorthand for @attr(value="value").
if (paramSpecByName == null) {
validator.error("Attribute doesn't take a parameter.", Literals.ATTRIBUTE__ATTR_NAME);
return;
}
AttrParamSpec valueSpec = paramSpecByName.get(VALUE_ATTR);
if (valueSpec == null) {
validator.error("Attribute doesn't have a 'value' parameter.", Literals.ATTR_PARM__NAME);
Expand All @@ -75,47 +85,55 @@ public void check(LFValidator validator, Attribute attr) {
valueSpec.check(validator, attr.getAttrParms().get(0));
seen = Set.of(VALUE_ATTR);
} else {
// Process multiple attributes, each of which has to be named.
// Process multiple parameters, each of which has to be named.
seen = processNamedAttrParms(validator, attr);
}

// Check if there are any missing parameters required by this attribute.
Map<String, AttrParamSpec> missingParams = new HashMap<>(paramSpecByName);
missingParams.keySet().removeAll(seen);
missingParams.forEach((name, paramSpec) -> {
if (!paramSpec.isOptional()) {
validator.error("Missing required attribute parameter '" + name + "'.", Literals.ATTRIBUTE__ATTR_PARMS);
}
});
if (paramSpecByName != null) {
Map<String, AttrParamSpec> missingParams = new HashMap<>(paramSpecByName);
missingParams.keySet().removeAll(seen);
missingParams.forEach((name, paramSpec) -> {
if (!paramSpec.isOptional()) {
validator.error("Missing required attribute parameter '" + name + "'.", Literals.ATTRIBUTE__ATTR_PARMS);
}
});
}
}

/**
* Check if the attribute parameters are named, whether
* Check whether the attribute parameters are named, whether
* these names are known, and whether the named parameters
* conform to the param spec (whether the param has the
* right type, etc.).
*
* @param validator The current validator in use
* @param attr The attribute being checked
* @return A set of named attribute parameters the user provides
* @param validator The current validator in use.
* @param attr The attribute being checked.
* @return A set of named attribute parameters the user provides.
*/
private Set<String> processNamedAttrParms(LFValidator validator, Attribute attr) {
Set<String> seen = new HashSet<>();
for (AttrParm parm : attr.getAttrParms()) {
if (parm.getName() == null) {
validator.error("Missing name for attribute parameter.", Literals.ATTRIBUTE__ATTR_NAME);
continue;
}

AttrParamSpec parmSpec = paramSpecByName.get(parm.getName());
if (parmSpec == null) {
validator.error("\"" + parm.getName() + "\"" + " is an unknown attribute parameter.",
Literals.ATTRIBUTE__ATTR_NAME);
continue;
if (attr.getAttrParms() != null) {
for (AttrParm parm : attr.getAttrParms()) {
if (paramSpecByName == null) {
validator.error("Attribute does not take parameters.", Literals.ATTRIBUTE__ATTR_NAME);
break;
}
if (parm.getName() == null) {
validator.error("Missing name for attribute parameter.", Literals.ATTRIBUTE__ATTR_NAME);
continue;
}

AttrParamSpec parmSpec = paramSpecByName.get(parm.getName());
if (parmSpec == null) {
validator.error("\"" + parm.getName() + "\"" + " is an unknown attribute parameter.",
Literals.ATTRIBUTE__ATTR_NAME);
continue;
}
// Check whether a parameter conforms to its spec.
parmSpec.check(validator, parm);
seen.add(parm.getName());
}
// Check whether a parameter conforms to its spec.
parmSpec.check(validator, parm);
seen.add(parm.getName());
}
return seen;
}
Expand Down Expand Up @@ -162,7 +180,6 @@ public void check(LFValidator validator, AttrParm parm) {
}
break;
}

}
}

Expand All @@ -185,5 +202,7 @@ enum AttrParamType {
ATTRIBUTE_SPECS_BY_NAME.put("label", new AttributeSpec(
List.of(new AttrParamSpec(AttributeSpec.VALUE_ATTR, AttrParamType.STRING, null))
));
// @sparse
ATTRIBUTE_SPECS_BY_NAME.put("sparse", new AttributeSpec(null));
}
}
6 changes: 0 additions & 6 deletions org.lflang/src/org/lflang/validation/LFValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,10 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
Expand All @@ -58,7 +56,6 @@
import org.eclipse.xtext.validation.Check;
import org.eclipse.xtext.validation.CheckType;
import org.eclipse.xtext.validation.ValidationMessageAcceptor;

import org.lflang.ASTUtils;
import org.lflang.ModelInfo;
import org.lflang.Target;
Expand All @@ -69,7 +66,6 @@
import org.lflang.lf.Action;
import org.lflang.lf.ActionOrigin;
import org.lflang.lf.Assignment;
import org.lflang.lf.AttrParm;
import org.lflang.lf.Attribute;
import org.lflang.lf.BuiltinTrigger;
import org.lflang.lf.BuiltinTriggerRef;
Expand Down Expand Up @@ -114,8 +110,6 @@
import org.lflang.lf.WidthSpec;
import org.lflang.lf.WidthTerm;
import org.lflang.util.FileUtil;
import org.lflang.validation.AttributeSpec.AttrParamSpec;
import org.lflang.validation.AttributeSpec.AttrParamType;

import com.google.inject.Inject;

Expand Down
Loading