blob: 4ea70eae10cefa4b0ab9bd0de979bc66f5f78277 [file] [log] [blame]
/**
********************************************************************************
* Copyright (c) 2020-2021 Robert Bosch GmbH.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Robert Bosch GmbH - initial API and implementation
********************************************************************************
*/
package org.eclipse.app4mc.slg.linux.generators
import java.util.HashMap
import java.util.Properties
import org.eclipse.app4mc.amalthea.model.LabelAccess
import org.eclipse.app4mc.amalthea.model.MinAvgMaxStatistic
import org.eclipse.app4mc.amalthea.model.Runnable
import org.eclipse.app4mc.amalthea.model.SingleValueStatistic
import org.eclipse.app4mc.amalthea.model.StringObject
import org.eclipse.app4mc.amalthea.model.Ticks
import org.eclipse.app4mc.amalthea.model.impl.CustomPropertyImpl
import org.eclipse.app4mc.slg.config.ConfigModel
import org.eclipse.app4mc.slg.config.PlatformArchitecture
class LinuxRunnableGenerator {
// Suppress default constructor
private new() {
throw new IllegalStateException("Utility class");
}
def static String snippetSrcStart(ConfigModel configModel)
'''
#include "runnables.h"
#include "codesnippets.h"
«val PlatformArchitecture platformArchitectureType = configModel.getPlatformArchitectureType()»
«IF platformArchitectureType!==null» #define __«platformArchitectureType.getName»__«ENDIF»
void ticks(int numberTicks){
# if defined (__x86_64__)
for(int i = 0; i < numberTicks; i++){
«FOR k : 1..400»
__asm volatile("nop");
«ENDFOR»
}
# elif defined (__x86_32__)
for(int i = 0; i < numberTicks; i++){
«FOR k : 1..400»
__asm volatile("mov r0, r0");
«ENDFOR»
}
# elif defined (__aarch64__)
for(int i = 0; i < numberTicks; i++){
«FOR k : 1..400»
__asm volatile("mov x0, x0");
«ENDFOR»
}
#endif
}
'''
def static String snippetSrcBody(Runnable runnable, String codeString, boolean enableExtCode)
'''
//Runnable «runnable?.name»
void run_«runnable?.name»(char* coreName){
«codeString»
}
'''
def static String snippetIncStart()
'''
#include "codesnippets.h"
void ticks(int numberTicks);
'''
/**
* This method is used to insert Synthetic code by considering the label-access's, Ticks (default and the PU specific i.e. extended ones)
*/
static def String syntheticLoad(Runnable runnable, boolean experimental, Properties properties, boolean enableExtCode ) {
val calc=new Calculation();
val StringBuffer codeHookFunctionsBuffer = new StringBuffer // custom property
val StringBuffer codeHookFunctionsOverwriteBuffer = new StringBuffer // custom property with overwrite
updateContent(runnable, calc, properties, codeHookFunctionsBuffer, codeHookFunctionsOverwriteBuffer)
val buffer = '''
«IF enableExtCode»
«IF codeHookFunctionsOverwriteBuffer.length() == 0»
«codeHookFunctionsBuffer»
«ELSE»
«codeHookFunctionsOverwriteBuffer»
«ENDIF»
«ENDIF»
«IF !enableExtCode || codeHookFunctionsOverwriteBuffer.length() == 0» ««« // write the runnable's code either when there is no external code or when the codehook is not of the overwrite type.
«var extendedFound=false»
«FOR puName : calc.ticksSumMap.keySet.sort»
«IF !puName.equals("default")»
«IF extendedFound »else«ENDIF» if(strcmp(coreName,"«puName»")==0){
«syntheticLoadContentForEachPU( experimental, calc,puName)»
}
«{extendedFound=true;""}»
«ENDIF»
«ENDFOR»
«IF calc.ticksSumMap.containsKey("default")»
«val value=syntheticLoadContentForEachPU( experimental, calc,"default" )»
«IF value!==null && value.length>0»
«IF extendedFound »else«ENDIF»
{
«value»
}
«ENDIF»
«ENDIF»
«ENDIF»
'''
return buffer
}
def static updateContent(Runnable runnable, Calculation calculation, Properties properties, StringBuffer codehookFct, StringBuffer codehookFctOverwrite) {
calculation.ticksSumMap.put("default", 0)
runnable?.activityGraph?.eContents?.forEach [ item |
if (item instanceof Ticks) {
calculation.ticksSumMap.put("default", calculation.ticksSumMap.get("default") + item?.^default?.average?.intValue)
val puDefinitions = item?.extended?.keySet
for (puDefinition : puDefinitions) {
if (puDefinition !== null) {
if (!calculation.ticksSumMap.containsKey(puDefinition.name)) {
calculation.ticksSumMap.put(puDefinition.name, 0)
}
calculation.ticksSumMap.put(puDefinition.name, calculation.ticksSumMap.get(puDefinition.name) +
item?.extended?.get(puDefinition)?.average?.intValue)
}
}
} else if (item instanceof LabelAccess) {
var Float value = Float.parseFloat(
properties.getOrDefault("labelAccessStatisticValueDefault", "1.0F").toString)
val labelStatistic = item.statistic
if (labelStatistic !== null) {
var labelStatisticValue = labelStatistic.value
if (labelStatisticValue instanceof SingleValueStatistic) {
value = labelStatisticValue.value
} else if (labelStatisticValue instanceof MinAvgMaxStatistic) {
// TODO: provide a configuration option, to select appropriate value from labelStatistic (min/max/avg)
// right now considering the average value
value = labelStatisticValue.avg
}
}
if (item.access.toString == "read") {
calculation.readsSum += value.intValue
} else if (item.access.toString == "write") {
calculation.writesSum += value.intValue
}
}
else if (item instanceof CustomPropertyImpl) // custom property
{
if (item.getKey.equals("codehook_overwrite")){
val value = item.getValue
if(value instanceof StringObject){
codehookFctOverwrite.append((value.value)+ ";")
codehookFctOverwrite.append(System.getProperty("line.separator"))
codehookFctOverwrite.append(System.getProperty("line.separator"))
}
// println(item.value)
}
else if (item.getKey.equals("codehook")){
val value = item.getValue
if(value instanceof StringObject){
codehookFct.append((value.value)+ ";")
codehookFct.append(System.getProperty("line.separator"))
codehookFct.append(System.getProperty("line.separator"))
}
// println(item.value)
}
}
]
}
static private def String syntheticLoadContentForEachPU( boolean experimental, Calculation calc,String puName) {
calc.resetCalculatedTicksSum
calc.calculatedTicksSum = calc.ticksSumMap.get(puName)
if (calc.readsSum !== 0 && calc.readsSum < 10) {
calc.readsSum = 10
}
if (calc.writesSum != 0 && calc.writesSum < 10) {
calc.writesSum = 10
}
calc.matchingCodeSnippetIdsBuffer = ""
performCodeSnippetMatching(experimental, calc)
// At this level 'matchingCodeSnippetIds' variable-> has the names of the algorithms which are matching to the given criteria of the Runnable
calc.calculatedTicksSum -= (calc.writesSum * 1.0171).intValue
calc.calculatedTicksSum -= (calc.readsSum * 1.0067).intValue
calc.calculatedTicksSum /= 400
calc.readsSum /= 10
calc.writesSum /= 10
if (calc.calculatedTicksSum> 0 && calc.matchingCodeSnippetIdsBuffer.length == 0) {
/*
* This code is executed if no code snippet is matching to the criteria of the Runnable (ticks/load/store)
* Only ATOMIC code is generated
*/
caseWithOnlyTicks(calc)
return (calc.matchingCodeSnippetIdsBuffer)
} else if ((calc.calculatedTicksSum <= 0 &&
(calc.writesSum > 0 || calc.readsSum > 0)) &&
calc.matchingCodeSnippetIdsBuffer.length == 0) {
/*
* This code is executed if no code snippet is matching to the criteria of the Runnable (ticks/load/store)
* Only ATOMIC code is generated
*/
caseWithOutTicksButWithLabelAccesses(calc)
return (calc.matchingCodeSnippetIdsBuffer)
}
if (calc.calculatedTicksSum > 0 && calc.matchingCodeSnippetIdsBuffer.length != 0) {
/*
* This code is executed when certain code snippets matching to the criteria of the Runnable(ticks/load/store) are found.
*/
caseWithTicksAndMatchingCodeSnippets(calc)
return (calc.matchingCodeSnippetIdsBuffer)
}
}
/**
* This method is used to check if the load specified at the Runnable is matching with any of the pre-defined code-snippet
*/
static private def void performCodeSnippetMatching(boolean experimental, Calculation calc) {
var int i
var boolean added = false
var codesnippets = LinuxRealisiticSyntheticGenerator.getCodeSnippets()
// CodeSnippet matching algorithm is enabled only if the switch 'experimentalCodeSnippetMatching' is supplied
if (experimental) { // properties.getProperty("experimentalCodeSnippetMatching")
for (i = 0; i < codesnippets.length; i++) {
if (i == 0) {
added = false
}
var int ticks_net = codesnippets.get(i).ticks - codesnippets.get(i).stallsload -
codesnippets.get(i).stallsstore
// This code is used to check which code snippets matches to the given Runnable (based on the ticks)
if (ticks_net < calc.calculatedTicksSum &&
codesnippets.get(i).reads < calc.readsSum &&
codesnippets.get(i).writes < calc.writesSum) {
calc.matchingCodeSnippetIdsBuffer += codesnippets.get(i).name + "();/\n"+System.getProperty("line.separator")
calc.calculatedTicksSum -= ticks_net
calc.readsSum -= codesnippets.get(i).reads
calc.writesSum -= codesnippets.get(i).writes
added = true
}
if (added && i == codesnippets.length - 1) {
i = -1
}
}
}
}
/**
* This method is used to generate the synthetic code for Runnable in the case when Ticks are present, and the 'synthetic code snippets are matching'
*/
static private def void caseWithTicksAndMatchingCodeSnippets(Calculation calc) {
var String[] matchingCodeSnippetsIdsArray = calc.matchingCodeSnippetIdsBuffer.split("/")
var int numberOfCodeSnippets = matchingCodeSnippetsIdsArray.length
calc.writesSum /= numberOfCodeSnippets
calc.readsSum /= numberOfCodeSnippets
calc.calculatedTicksSum /= numberOfCodeSnippets
calc.matchingCodeSnippetIdsBuffer = '''
volatile int arraymb[250000];
int i;
'''
matchingCodeSnippetsIdsArray.forEach [ codeSumPart |
// inserting code snippet call e.g: bubblesort();
calc.matchingCodeSnippetIdsBuffer += codeSumPart
var int randomNumber1 = (Math.random() * 200000).intValue
var int randomNumber2 = (Math.random() * 200000).intValue
calc.matchingCodeSnippetIdsBuffer += '''
«IF calc.writesSum > 0 || calc.readsSum > 0»
«IF calc.writesSum > 0»
for (i = 0; i < «calc.writesSum»; i++){
«FOR k : 0..9»
arraymb[«(Math.random()*5000).intValue»] = «randomNumber1»;
«ENDFOR»
}
«ENDIF»
«IF calc.readsSum > 0»
for (i = 0; i < «calc.readsSum»; i++){
«FOR k : 0..9»
arraymb[«(Math.random()*5000).intValue»] == «randomNumber2»;
«ENDFOR»
}
«ENDIF»
«ENDIF»
ticks(«calc.calculatedTicksSum»);
'''
]
}
/**
* This method is used to generate the synthetic code for Runnable in the case when Ticks and LabelAccess's are present
*/
static private def String caseWithOutTicksButWithLabelAccesses(Calculation calc) {
var int randomNumber1 = (Math.random() * 200000).intValue
var int randomNumber2 = (Math.random() * 200000).intValue
calc.matchingCodeSnippetIdsBuffer = '''
«IF calc.writesSum > 0 || calc.readsSum > 0»
volatile int arraymb[250000];
int i;
«IF calc.writesSum > 0»
for (i = 0; i < «calc.writesSum»; i++){
«FOR k : 0..9»
arraymb[«(Math.random()*5000).intValue»] = «randomNumber1»;
«ENDFOR»
}
«ENDIF»
«IF calc.readsSum > 0»
for (i = 0; i < «calc.readsSum»; i++){
«FOR k : 0..9»
arraymb[«(Math.random()*5000).intValue»] == «randomNumber2»;
«ENDFOR»
}
«ENDIF»
«ENDIF»
'''
}
/**
* This method is used to generate the synthetic code for Runnable in the case when only Ticks are present
*/
static private def String caseWithOnlyTicks(Calculation calc) {
var int randomNumber1 = (Math.random() * 200000).intValue
var int randomNumber2 = (Math.random() * 200000).intValue
calc.matchingCodeSnippetIdsBuffer = '''
«IF calc.writesSum > 0 || calc.readsSum > 0»
volatile int arraymb[250000];
int i;
«IF calc.writesSum > 0»
for (i = 0; i < «calc.writesSum»; i++){
«FOR k : 0..9»
arraymb[«(Math.random()*5000).intValue»] = «randomNumber1»;
«ENDFOR»
}
«ENDIF»
«IF calc.readsSum > 0»
for (i = 0; i < «calc.readsSum»; i++){
«FOR k : 0..9»
arraymb[«(Math.random()*5000).intValue»] == «randomNumber2»;
«ENDFOR»
}
«ENDIF»
«ENDIF»
ticks(«calc.calculatedTicksSum»);
'''
}
static class Calculation {
public var int calculatedTicksSum = 0
public var int readsSum = 0 // extracted from runnable
public var int writesSum = 0 // extracted from runnable
public var String matchingCodeSnippetIdsBuffer = ""
public val ticksSumMap = new HashMap<String, Integer>
def resetCalculatedTicksSum(){
calculatedTicksSum=0;
}
}
}