Skip to content

Commit

Permalink
Add initial implementation for live variable analysis.
Browse files Browse the repository at this point in the history
  • Loading branch information
m-carrasco committed Feb 11, 2024
1 parent ec84102 commit d516eb2
Show file tree
Hide file tree
Showing 11 changed files with 287 additions and 6 deletions.
8 changes: 7 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,10 @@ ci/docker/Dockerfile
ci/docker/Dockerfile.base
ci/build-image.sh

integration-test/output
integration-test/output
net-ssa-cli/obj
net-ssa-lib/obj
unit-tests/obj
net-ssa-cli/bin
net-ssa-lib/bin
unit-tests/bin
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ jobs:
dotnet test --verbosity normal
- name: Generate nuget packages
run: |
./ci/nuget-pack.sh "${{ steps.gitversion.outputs.AssemblySemVer }}" "${{ steps.gitversion.outputs.AssemblySemVer }}" "${{ steps.gitversion.outputs.InformationalVersion }}" "${{ steps.gitversion.outputs.AssemblySemVer }}"
./ci/nuget-pack.sh "${{ steps.gitversion.outputs.AssemblySemVer }}" "${{ steps.gitversion.outputs.AssemblySemVer }}" "${{ steps.gitversion.outputs.InformationalVersion }}" "${{ steps.gitversion.outputs.AssemblySemVer }}" "build/bin/net-ssa/package"
dotnet nuget push build/bin/net-ssa/package/net-ssa-lib.${{ steps.gitversion.outputs.NuGetVersionV2 }}.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json
mirror:
Expand Down
16 changes: 16 additions & 0 deletions ci/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash
set -e

CURRENT_DIR=$(dirname "$(readlink -f "$0")")

pushd $CURRENT_DIR/..

rm -rf net-ssa-cli/obj net-ssa-lib/obj unit-tests/obj net-ssa-cli/bin net-ssa-lib/bin unit-tests/bin

dotnet clean
rm -rf /tmp/build

dotnet build
./ci/nuget-pack.sh "0.0.0" "0.0.0" "0.0.0" "0.0.0" "/tmp/build/bin/net-ssa/package"

popd
2 changes: 1 addition & 1 deletion ci/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ FROM ${BASE_IMAGE}

COPY --chown=ubuntu:mygroup . ${NET_SSA_SRC_DIR}

RUN dotnet build && \
RUN ./ci/build.sh && \
dotnet test --verbosity normal && \
lit ./integration-test -vv
4 changes: 3 additions & 1 deletion ci/docker/Dockerfile.base
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ COPY --chown=ubuntu:mygroup ./ci/ ${NET_SSA_SRC_DIR}/ci
RUN sudo ${NET_SSA_SRC_DIR}/ci/install-souffle.sh && \
sudo ${NET_SSA_SRC_DIR}/ci/install-lit.sh && \
sudo ${NET_SSA_SRC_DIR}/ci/install-llvm.sh && \
sudo ${NET_SSA_SRC_DIR}/ci/install-mono.sh
sudo ${NET_SSA_SRC_DIR}/ci/install-mono.sh && \
dotnet tool install -g dotnet-repl

ENV PATH="$PATH:/home/ubuntu/.dotnet/tools"

WORKDIR ${NET_SSA_SRC_DIR}
3 changes: 2 additions & 1 deletion ci/nuget-pack.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ VERSION=$1
ASSEMBLY_VERSION=$2
INFORMATIONAL_VERSION=$3
PACKAGE_VERSION=$4
OUTPUT_DIR=$5

pushd $CURRENT_DIR/..

dotnet pack net-ssa.sln -o:build/bin/net-ssa/package --include-symbols --include-source /p:Version=$VERSION /p:AssemblyVersion=$ASSEMBLY_VERSION /p:InformationalVersion=$INFORMATIONAL_VERSION /p:PackageVersion=$PACKAGE_VERSION
dotnet pack net-ssa.sln -o:$OUTPUT_DIR --include-symbols --include-source /p:Version=$VERSION /p:AssemblyVersion=$ASSEMBLY_VERSION /p:InformationalVersion=$INFORMATIONAL_VERSION /p:PackageVersion=$PACKAGE_VERSION

popd
3 changes: 2 additions & 1 deletion integration-test/lit.cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def _get_llvm_bindir():
config.test_build_root = os.path.join(config.test_source_root, "output")
if not os.path.exists(config.test_build_root):
os.mkdir(config.test_build_root)
config.test_exec_root = config.test_build_root

config.substitutions.append(('%mono', config.mono_bin))
config.substitutions.append(('%mcs', config.mcs_bin))
Expand All @@ -60,7 +61,7 @@ def _get_llvm_bindir():
config.substitutions.append(('%FileCheck', os.path.join(config.llvm_bin_dir, "FileCheck")))
config.substitutions.append(('%test-resources-dir', os.path.join(config.my_src_root, "test-resources")))

env_vars = {'DOTNET_ROOT'}
env_vars = {'DOTNET_ROOT', 'HOME'}
for e in env_vars:
if e in os.environ:
config.environment[e] = os.environ[e]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;
class HelloWorld
{
static void Main()
{
for (int i=0; i < 10; i++){
Console.WriteLine(i);
}
}
}
88 changes: 88 additions & 0 deletions integration-test/net-ssa-lib/live-variables/test0/test.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// RUN: %mcs -out:%T/Test.dll %S/program.cs.exclude
// RUN: (export BINARY_PATH=%T/Test.dll && dotnet repl --output-path %t.trx --run %s --exit-after-run)

#i "nuget:/tmp/build/bin/net-ssa/package"
#r "nuget: net-ssa-lib, 0.0.0"
#r "nuget: Mono.Cecil, 0.11.3"
#r "nuget: NUnit, 3.12.0"

using System;
using System.Collections.Generic;

using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;

using NetSsa.Analyses;
using NetSsa.Instructions;

using NUnit.Framework;
using System.Linq;

public static String GetRegisters(IList<Register> registers, BitArray liveVariables){
String result = "";
for (int i=0; i < liveVariables.Length; i++){
if (liveVariables[i]){
result += registers[i].Name + " ";
}
}
return result.Trim();
}

AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(Environment.GetEnvironmentVariable("BINARY_PATH"));

var method = assembly.MainModule.GetType("HelloWorld").GetMethods().First();
Mono.Cecil.Cil.MethodBody body = method.Body;
IRBody irBody = Unstacker.Compute(body);
ControlFlowGraph cfg = new ControlFlowGraph(irBody);
LiveVariableAnalysis lva = new LiveVariableAnalysis(cfg);
lva.Flow();

/*
L_0000: label
L_0001: nop
L_0002: s0 = ldc.i4.0
L_0003: l0 = stloc.0 [s0] -- s0
L_0004: br L_000c
L_0005: label
L_0006: s0 = ldloc.0 [l0]
L_0007: call System.Void System.Console::WriteLine(System.Int32) [s0] -- s0
L_0008: s0 = ldloc.0 [l0]
L_0009: s1 = ldc.i4.1 ---- [s0]
L_000a: s0 = add [s0, s1] --- [s0, s1]
L_000b: l0 = stloc.0 [s0] --- [s0]
L_000c: label
L_000d: s0 = ldloc.0 [l0]
L_000e: s1 = ldc.i4.s 10 ---- [s0]
L_000f: blt L_0005 [s0, s1] -- s0 s1
L_0010: label
L_0011: ret
*/

Assert.AreEqual(irBody.Instructions.Count, 0x12);

Dictionary<int, string> results = new Dictionary<int, string>()
{
{ 0x0, "" },
{ 0x1, "" },
{ 0x2, "" },
{ 0x3, "s0" },
{ 0x4, "" },
{ 0x5, "" },
{ 0x6, "" },
{ 0x7, "s0" },
{ 0x8, "" },
{ 0x9, "s0" },
{ 0xa, "s0 s1" },
{ 0xb, "s0" },
{ 0xc, "" },
{ 0xd, "" },
{ 0xe, "s0" },
{ 0xf, "s0 s1" },
{ 0x10, "" },
{ 0x11, "" },
};

for (int i=0; i < 0x12; i++){
Assert.AreEqual(results[i], GetRegisters(irBody.Registers, lva.IN[irBody.Instructions.ElementAt(i)]), i.ToString("x"));
}
76 changes: 76 additions & 0 deletions net-ssa-lib/analyses/DataFlowAnalysis.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System.Collections.Generic;
using NetSsa.Instructions;
using System.Linq;

namespace NetSsa.Analyses
{
public abstract class BackwardsDataFlowAnalysis<Data>
{
public BackwardsDataFlowAnalysis(ControlFlowGraph cfg){
this.cfg = cfg;
this.irBody = cfg.IRBody;
}

protected ControlFlowGraph cfg;
protected IRBody irBody;

protected abstract Data Meet(Data a, Data b); // This is expected to create a copy of the input data
protected virtual Data MeetSuccessors(TacInstruction leader){
IList<TacInstruction> successors = cfg.BasicBlockSuccessors(leader);
Data result = IN[successors[0]];
for (int i = 1; i < successors.Count; i++){
result = Meet(result, IN[successors[i]]);
}
return result;
}

protected abstract Data Gen(TacInstruction leader);
protected abstract Data Kill(TacInstruction leader);
protected abstract bool TransferInstruction(TacInstruction instruction, Data incomingData);

protected virtual void Transfer(TacInstruction leader, Data incomingData, ref bool changed){
IList<TacInstruction> instructions = cfg.BasicBlockInstructions(leader).ToList();

bool currentChanged = TransferInstruction(instructions[instructions.Count-1], incomingData);
for (int i=instructions.Count-2; i >= 0; i--){
currentChanged = currentChanged || TransferInstruction(instructions[i], IN[instructions[i+1]]);
}

changed = currentChanged;
}

protected abstract Data InitBasicBlock(TacInstruction leader);

public IDictionary<TacInstruction, Data> IN = new Dictionary<TacInstruction, Data>();
public IDictionary<TacInstruction, Data> OUT = new Dictionary<TacInstruction, Data>();

protected virtual IEnumerable<TacInstruction> GetExitBasicBlocks(){
foreach (TacInstruction leader in cfg.Leaders()){
if (cfg.BasicBlockSuccessors(leader).Count == 0){
yield return leader;
}
}
}

protected void InitializeBasicBlocks(){
foreach (TacInstruction leader in cfg.Leaders()){
foreach (TacInstruction instruction in cfg.BasicBlockInstructions(leader)){
IN[instruction] = InitBasicBlock(leader);
}
}
}

public void Flow(){
InitializeBasicBlocks();
bool inChanged;
do {
inChanged = false;
foreach (TacInstruction leader in cfg.Leaders().Except(GetExitBasicBlocks())){
OUT[leader] = MeetSuccessors(leader);
Transfer(leader, OUT[leader], ref inChanged);
}
} while (inChanged);
}
}

}
81 changes: 81 additions & 0 deletions net-ssa-lib/analyses/LiveVariableAnalysis.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System.Collections.Generic;
using System.Collections;
using System;
using NetSsa.Instructions;
using System.Linq;

namespace NetSsa.Analyses{
public class LiveVariableAnalysis : BackwardsDataFlowAnalysis<BitArray> {
public LiveVariableAnalysis(ControlFlowGraph cfg) : base(cfg) {}

private int NumberOfRegisters(){
return irBody.Registers.Count;
}

// TO-DO: Find faster implementation
private int RegisterIndex(Register register){
return irBody.Registers.IndexOf(register);
}

// TO-DO: Find faster implementation
private bool AreEqual(BitArray a, BitArray b) {
if (a.Length != b.Length){
return false;
}

for (int i=0; i < a.Length; i++){
if (a[i] != b[i]){
return false;
}
}

return true;
}

protected override BitArray Meet(BitArray a, BitArray b) {
return a.Or(b);
}

protected override BitArray Gen(TacInstruction instruction) {
BitArray result = new BitArray(NumberOfRegisters());
foreach (int index in instruction.Operands.OfType<Register>().Select(r => RegisterIndex(r))){
result.Set(index, true);
}

return result;
}

protected override BitArray Kill(TacInstruction instruction) {
BitArray result = new BitArray(NumberOfRegisters());

if (instruction.Result is Register){
result.Set(RegisterIndex((Register)instruction.Result), true);
}

return result;
}

protected override bool TransferInstruction(TacInstruction instruction, BitArray incomingData){
BitArray gen = Gen(instruction);
BitArray kill = Kill(instruction);

// The difference of two sets S T is computed by
// complementing the bit vector of T, and then taking the logical AND of that
// complement, with the bit vector for S.

BitArray newValue = gen.Or(kill.Not().And(incomingData));

BitArray old = IN[instruction];
if (AreEqual(old, newValue)){
return false;
} else {
IN[instruction] = newValue;
return true;
}
}

protected override BitArray InitBasicBlock(TacInstruction leader){
return new BitArray(NumberOfRegisters());
}
}
}

0 comments on commit d516eb2

Please sign in to comment.