Skip to content

Commit d516eb2

Browse files
committed
Add initial implementation for live variable analysis.
1 parent ec84102 commit d516eb2

File tree

11 files changed

+287
-6
lines changed

11 files changed

+287
-6
lines changed

.dockerignore

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,10 @@ ci/docker/Dockerfile
44
ci/docker/Dockerfile.base
55
ci/build-image.sh
66

7-
integration-test/output
7+
integration-test/output
8+
net-ssa-cli/obj
9+
net-ssa-lib/obj
10+
unit-tests/obj
11+
net-ssa-cli/bin
12+
net-ssa-lib/bin
13+
unit-tests/bin

.github/workflows/build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ jobs:
145145
dotnet test --verbosity normal
146146
- name: Generate nuget packages
147147
run: |
148-
./ci/nuget-pack.sh "${{ steps.gitversion.outputs.AssemblySemVer }}" "${{ steps.gitversion.outputs.AssemblySemVer }}" "${{ steps.gitversion.outputs.InformationalVersion }}" "${{ steps.gitversion.outputs.AssemblySemVer }}"
148+
./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"
149149
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
150150
151151
mirror:

ci/build.sh

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/bin/bash
2+
set -e
3+
4+
CURRENT_DIR=$(dirname "$(readlink -f "$0")")
5+
6+
pushd $CURRENT_DIR/..
7+
8+
rm -rf net-ssa-cli/obj net-ssa-lib/obj unit-tests/obj net-ssa-cli/bin net-ssa-lib/bin unit-tests/bin
9+
10+
dotnet clean
11+
rm -rf /tmp/build
12+
13+
dotnet build
14+
./ci/nuget-pack.sh "0.0.0" "0.0.0" "0.0.0" "0.0.0" "/tmp/build/bin/net-ssa/package"
15+
16+
popd

ci/docker/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ FROM ${BASE_IMAGE}
33

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

6-
RUN dotnet build && \
6+
RUN ./ci/build.sh && \
77
dotnet test --verbosity normal && \
88
lit ./integration-test -vv

ci/docker/Dockerfile.base

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ COPY --chown=ubuntu:mygroup ./ci/ ${NET_SSA_SRC_DIR}/ci
2222
RUN sudo ${NET_SSA_SRC_DIR}/ci/install-souffle.sh && \
2323
sudo ${NET_SSA_SRC_DIR}/ci/install-lit.sh && \
2424
sudo ${NET_SSA_SRC_DIR}/ci/install-llvm.sh && \
25-
sudo ${NET_SSA_SRC_DIR}/ci/install-mono.sh
25+
sudo ${NET_SSA_SRC_DIR}/ci/install-mono.sh && \
26+
dotnet tool install -g dotnet-repl
2627

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

2830
WORKDIR ${NET_SSA_SRC_DIR}

ci/nuget-pack.sh

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ VERSION=$1
77
ASSEMBLY_VERSION=$2
88
INFORMATIONAL_VERSION=$3
99
PACKAGE_VERSION=$4
10+
OUTPUT_DIR=$5
1011

1112
pushd $CURRENT_DIR/..
1213

13-
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
14+
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
1415

1516
popd

integration-test/lit.cfg.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ def _get_llvm_bindir():
5151
config.test_build_root = os.path.join(config.test_source_root, "output")
5252
if not os.path.exists(config.test_build_root):
5353
os.mkdir(config.test_build_root)
54+
config.test_exec_root = config.test_build_root
5455

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

63-
env_vars = {'DOTNET_ROOT'}
64+
env_vars = {'DOTNET_ROOT', 'HOME'}
6465
for e in env_vars:
6566
if e in os.environ:
6667
config.environment[e] = os.environ[e]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System;
2+
class HelloWorld
3+
{
4+
static void Main()
5+
{
6+
for (int i=0; i < 10; i++){
7+
Console.WriteLine(i);
8+
}
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// RUN: %mcs -out:%T/Test.dll %S/program.cs.exclude
2+
// RUN: (export BINARY_PATH=%T/Test.dll && dotnet repl --output-path %t.trx --run %s --exit-after-run)
3+
4+
#i "nuget:/tmp/build/bin/net-ssa/package"
5+
#r "nuget: net-ssa-lib, 0.0.0"
6+
#r "nuget: Mono.Cecil, 0.11.3"
7+
#r "nuget: NUnit, 3.12.0"
8+
9+
using System;
10+
using System.Collections.Generic;
11+
12+
using Mono.Cecil;
13+
using Mono.Cecil.Cil;
14+
using Mono.Cecil.Rocks;
15+
16+
using NetSsa.Analyses;
17+
using NetSsa.Instructions;
18+
19+
using NUnit.Framework;
20+
using System.Linq;
21+
22+
public static String GetRegisters(IList<Register> registers, BitArray liveVariables){
23+
String result = "";
24+
for (int i=0; i < liveVariables.Length; i++){
25+
if (liveVariables[i]){
26+
result += registers[i].Name + " ";
27+
}
28+
}
29+
return result.Trim();
30+
}
31+
32+
AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(Environment.GetEnvironmentVariable("BINARY_PATH"));
33+
34+
var method = assembly.MainModule.GetType("HelloWorld").GetMethods().First();
35+
Mono.Cecil.Cil.MethodBody body = method.Body;
36+
IRBody irBody = Unstacker.Compute(body);
37+
ControlFlowGraph cfg = new ControlFlowGraph(irBody);
38+
LiveVariableAnalysis lva = new LiveVariableAnalysis(cfg);
39+
lva.Flow();
40+
41+
/*
42+
L_0000: label
43+
L_0001: nop
44+
L_0002: s0 = ldc.i4.0
45+
L_0003: l0 = stloc.0 [s0] -- s0
46+
L_0004: br L_000c
47+
L_0005: label
48+
L_0006: s0 = ldloc.0 [l0]
49+
L_0007: call System.Void System.Console::WriteLine(System.Int32) [s0] -- s0
50+
L_0008: s0 = ldloc.0 [l0]
51+
L_0009: s1 = ldc.i4.1 ---- [s0]
52+
L_000a: s0 = add [s0, s1] --- [s0, s1]
53+
L_000b: l0 = stloc.0 [s0] --- [s0]
54+
L_000c: label
55+
L_000d: s0 = ldloc.0 [l0]
56+
L_000e: s1 = ldc.i4.s 10 ---- [s0]
57+
L_000f: blt L_0005 [s0, s1] -- s0 s1
58+
L_0010: label
59+
L_0011: ret
60+
*/
61+
62+
Assert.AreEqual(irBody.Instructions.Count, 0x12);
63+
64+
Dictionary<int, string> results = new Dictionary<int, string>()
65+
{
66+
{ 0x0, "" },
67+
{ 0x1, "" },
68+
{ 0x2, "" },
69+
{ 0x3, "s0" },
70+
{ 0x4, "" },
71+
{ 0x5, "" },
72+
{ 0x6, "" },
73+
{ 0x7, "s0" },
74+
{ 0x8, "" },
75+
{ 0x9, "s0" },
76+
{ 0xa, "s0 s1" },
77+
{ 0xb, "s0" },
78+
{ 0xc, "" },
79+
{ 0xd, "" },
80+
{ 0xe, "s0" },
81+
{ 0xf, "s0 s1" },
82+
{ 0x10, "" },
83+
{ 0x11, "" },
84+
};
85+
86+
for (int i=0; i < 0x12; i++){
87+
Assert.AreEqual(results[i], GetRegisters(irBody.Registers, lva.IN[irBody.Instructions.ElementAt(i)]), i.ToString("x"));
88+
}
+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System.Collections.Generic;
2+
using NetSsa.Instructions;
3+
using System.Linq;
4+
5+
namespace NetSsa.Analyses
6+
{
7+
public abstract class BackwardsDataFlowAnalysis<Data>
8+
{
9+
public BackwardsDataFlowAnalysis(ControlFlowGraph cfg){
10+
this.cfg = cfg;
11+
this.irBody = cfg.IRBody;
12+
}
13+
14+
protected ControlFlowGraph cfg;
15+
protected IRBody irBody;
16+
17+
protected abstract Data Meet(Data a, Data b); // This is expected to create a copy of the input data
18+
protected virtual Data MeetSuccessors(TacInstruction leader){
19+
IList<TacInstruction> successors = cfg.BasicBlockSuccessors(leader);
20+
Data result = IN[successors[0]];
21+
for (int i = 1; i < successors.Count; i++){
22+
result = Meet(result, IN[successors[i]]);
23+
}
24+
return result;
25+
}
26+
27+
protected abstract Data Gen(TacInstruction leader);
28+
protected abstract Data Kill(TacInstruction leader);
29+
protected abstract bool TransferInstruction(TacInstruction instruction, Data incomingData);
30+
31+
protected virtual void Transfer(TacInstruction leader, Data incomingData, ref bool changed){
32+
IList<TacInstruction> instructions = cfg.BasicBlockInstructions(leader).ToList();
33+
34+
bool currentChanged = TransferInstruction(instructions[instructions.Count-1], incomingData);
35+
for (int i=instructions.Count-2; i >= 0; i--){
36+
currentChanged = currentChanged || TransferInstruction(instructions[i], IN[instructions[i+1]]);
37+
}
38+
39+
changed = currentChanged;
40+
}
41+
42+
protected abstract Data InitBasicBlock(TacInstruction leader);
43+
44+
public IDictionary<TacInstruction, Data> IN = new Dictionary<TacInstruction, Data>();
45+
public IDictionary<TacInstruction, Data> OUT = new Dictionary<TacInstruction, Data>();
46+
47+
protected virtual IEnumerable<TacInstruction> GetExitBasicBlocks(){
48+
foreach (TacInstruction leader in cfg.Leaders()){
49+
if (cfg.BasicBlockSuccessors(leader).Count == 0){
50+
yield return leader;
51+
}
52+
}
53+
}
54+
55+
protected void InitializeBasicBlocks(){
56+
foreach (TacInstruction leader in cfg.Leaders()){
57+
foreach (TacInstruction instruction in cfg.BasicBlockInstructions(leader)){
58+
IN[instruction] = InitBasicBlock(leader);
59+
}
60+
}
61+
}
62+
63+
public void Flow(){
64+
InitializeBasicBlocks();
65+
bool inChanged;
66+
do {
67+
inChanged = false;
68+
foreach (TacInstruction leader in cfg.Leaders().Except(GetExitBasicBlocks())){
69+
OUT[leader] = MeetSuccessors(leader);
70+
Transfer(leader, OUT[leader], ref inChanged);
71+
}
72+
} while (inChanged);
73+
}
74+
}
75+
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using System.Collections.Generic;
2+
using System.Collections;
3+
using System;
4+
using NetSsa.Instructions;
5+
using System.Linq;
6+
7+
namespace NetSsa.Analyses{
8+
public class LiveVariableAnalysis : BackwardsDataFlowAnalysis<BitArray> {
9+
public LiveVariableAnalysis(ControlFlowGraph cfg) : base(cfg) {}
10+
11+
private int NumberOfRegisters(){
12+
return irBody.Registers.Count;
13+
}
14+
15+
// TO-DO: Find faster implementation
16+
private int RegisterIndex(Register register){
17+
return irBody.Registers.IndexOf(register);
18+
}
19+
20+
// TO-DO: Find faster implementation
21+
private bool AreEqual(BitArray a, BitArray b) {
22+
if (a.Length != b.Length){
23+
return false;
24+
}
25+
26+
for (int i=0; i < a.Length; i++){
27+
if (a[i] != b[i]){
28+
return false;
29+
}
30+
}
31+
32+
return true;
33+
}
34+
35+
protected override BitArray Meet(BitArray a, BitArray b) {
36+
return a.Or(b);
37+
}
38+
39+
protected override BitArray Gen(TacInstruction instruction) {
40+
BitArray result = new BitArray(NumberOfRegisters());
41+
foreach (int index in instruction.Operands.OfType<Register>().Select(r => RegisterIndex(r))){
42+
result.Set(index, true);
43+
}
44+
45+
return result;
46+
}
47+
48+
protected override BitArray Kill(TacInstruction instruction) {
49+
BitArray result = new BitArray(NumberOfRegisters());
50+
51+
if (instruction.Result is Register){
52+
result.Set(RegisterIndex((Register)instruction.Result), true);
53+
}
54+
55+
return result;
56+
}
57+
58+
protected override bool TransferInstruction(TacInstruction instruction, BitArray incomingData){
59+
BitArray gen = Gen(instruction);
60+
BitArray kill = Kill(instruction);
61+
62+
// The difference of two sets S T is computed by
63+
// complementing the bit vector of T, and then taking the logical AND of that
64+
// complement, with the bit vector for S.
65+
66+
BitArray newValue = gen.Or(kill.Not().And(incomingData));
67+
68+
BitArray old = IN[instruction];
69+
if (AreEqual(old, newValue)){
70+
return false;
71+
} else {
72+
IN[instruction] = newValue;
73+
return true;
74+
}
75+
}
76+
77+
protected override BitArray InitBasicBlock(TacInstruction leader){
78+
return new BitArray(NumberOfRegisters());
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)