blob: 5466e5fa1bbe4dbf1283c1cfc5265fb62f865f96 [file] [log] [blame] [edit]
#include <iostream>
#include "analysis/cfg.h"
#include "analysis/lattice.h"
#include "analysis/liveness-transfer-function.h"
#include "analysis/monotone-analyzer.h"
#include "analysis/reaching-definitions-transfer-function.h"
#include "ir/find_all.h"
#include "print-test.h"
#include "wasm.h"
#include "gtest/gtest.h"
using namespace wasm;
using namespace wasm::analysis;
using CFGTest = PrintTest;
TEST_F(CFGTest, Print) {
auto moduleText = R"wasm(
(module
(func $foo
(drop
(i32.const 0)
)
(drop
(if (result i32)
(i32.const 1)
(block
(loop $loop
(br_if $loop
(i32.const 2)
)
)
(i32.const 3)
)
(return
(i32.const 4)
)
)
)
)
)
)wasm";
auto cfgText = R"cfg(;; preds: [], succs: [1, 5]
0:
0: i32.const 0
1: drop
2: i32.const 1
;; preds: [0], succs: [2]
1:
;; preds: [1, 2], succs: [3, 2]
2:
3: i32.const 2
4: br_if $loop
;; preds: [2], succs: [4]
3:
5: loop $loop
;; preds: [3], succs: [6]
4:
6: i32.const 3
7: block
;; preds: [0], succs: []
5:
8: i32.const 4
9: return
;; preds: [4], succs: []
6:
10: drop
11: block
)cfg";
Module wasm;
parseWast(wasm, moduleText);
CFG cfg = CFG::fromFunction(wasm.getFunction("foo"));
std::stringstream ss;
cfg.print(ss);
EXPECT_EQ(ss.str(), cfgText);
}
TEST_F(CFGTest, LinearLiveness) {
auto moduleText = R"wasm(
(module
(func $bar
(local $a (i32))
(local $b (i32))
(local $c (i32))
(local.set $a
(i32.const 1)
)
(drop
(local.get $a)
)
(local.set $b
(local.get $a)
)
(local.set $c
(i32.const 1)
)
(drop
(local.get $c)
)
)
)
)wasm";
auto analyzerText = R"analyzer(CFG Analyzer
CFG Block: 0
Input State: 000
Predecessors:
Successors:
Intermediate States (reverse order):
000
block
000
drop
000
local.get $2
001
local.set $2
000
i32.const 1
000
local.set $1
000
local.get $0
100
drop
100
local.get $0
100
local.set $0
000
i32.const 1
000
End
)analyzer";
Module wasm;
parseWast(wasm, moduleText);
CFG cfg = CFG::fromFunction(wasm.getFunction("bar"));
size_t numLocals = wasm.getFunction("bar")->getNumLocals();
FiniteIntPowersetLattice lattice(numLocals);
LivenessTransferFunction transferFunction;
MonotoneCFGAnalyzer<FiniteIntPowersetLattice, LivenessTransferFunction>
analyzer(lattice, transferFunction, cfg);
analyzer.evaluate();
std::stringstream ss;
analyzer.print(ss);
EXPECT_EQ(ss.str(), analyzerText);
}
TEST_F(CFGTest, NonlinearLiveness) {
auto moduleText = R"wasm(
(module
(func $bar
(local $a (i32))
(local $b (i32))
(local.set $a
(i32.const 1)
)
(if
(i32.eq
(local.get $a)
(i32.const 2)
)
(local.set $b
(i32.const 4)
)
(drop
(local.get $a)
)
)
)
)
)wasm";
auto analyzerText = R"analyzer(CFG Analyzer
CFG Block: 0
Input State: 10
Predecessors:
Successors: 1 2
Intermediate States (reverse order):
10
i32.eq
10
i32.const 2
10
local.get $0
10
local.set $0
00
i32.const 1
00
CFG Block: 1
Input State: 00
Predecessors: 0
Successors: 3
Intermediate States (reverse order):
00
local.set $1
00
i32.const 4
00
CFG Block: 2
Input State: 00
Predecessors: 0
Successors: 3
Intermediate States (reverse order):
00
drop
00
local.get $0
10
CFG Block: 3
Input State: 00
Predecessors: 2 1
Successors:
Intermediate States (reverse order):
00
block
00
End
)analyzer";
Module wasm;
parseWast(wasm, moduleText);
CFG cfg = CFG::fromFunction(wasm.getFunction("bar"));
size_t numLocals = wasm.getFunction("bar")->getNumLocals();
FiniteIntPowersetLattice lattice(numLocals);
LivenessTransferFunction transferFunction;
MonotoneCFGAnalyzer<FiniteIntPowersetLattice, LivenessTransferFunction>
analyzer(lattice, transferFunction, cfg);
analyzer.evaluate();
std::stringstream ss;
analyzer.print(ss);
EXPECT_EQ(ss.str(), analyzerText);
}
TEST_F(CFGTest, FinitePowersetLatticeFunctioning) {
std::vector<std::string> initialSet = {"a", "b", "c", "d", "e", "f"};
FinitePowersetLattice<std::string> lattice(std::move(initialSet));
auto element1 = lattice.getBottom();
EXPECT_TRUE(element1.isBottom());
EXPECT_FALSE(element1.isTop());
lattice.add(&element1, "c");
lattice.add(&element1, "d");
lattice.add(&element1, "a");
EXPECT_FALSE(element1.isBottom());
EXPECT_FALSE(element1.isTop());
auto element2 = element1;
lattice.remove(&element2, "c");
EXPECT_EQ(FinitePowersetLattice<std::string>::compare(element1, element2),
LatticeComparison::GREATER);
lattice.add(&element2, "f");
EXPECT_EQ(FinitePowersetLattice<std::string>::compare(element1, element2),
LatticeComparison::NO_RELATION);
std::stringstream ss;
element1.print(ss);
EXPECT_EQ(ss.str(), "101100");
ss.str(std::string());
element2.print(ss);
EXPECT_EQ(ss.str(), "100101");
ss.str(std::string());
element2.makeLeastUpperBound(element1);
element2.print(ss);
EXPECT_EQ(ss.str(), "101101");
}
TEST_F(CFGTest, LinearReachingDefinitions) {
auto moduleText = R"wasm(
(module
(func $bar
(local $a (i32))
(local $b (i32))
(local $c (i32))
(local.set $a
(i32.const 1)
)
(drop
(local.get $a)
)
(local.set $b
(local.get $a)
)
(drop
(local.get $c)
)
(local.set $c
(i32.const 1)
)
(local.set $a
(i32.const 2)
)
)
)
)wasm";
Module wasm;
parseWast(wasm, moduleText);
Function* func = wasm.getFunction("bar");
CFG cfg = CFG::fromFunction(func);
FindAll<LocalSet> setFinder(func->body);
for (size_t i = 0; i < func->getNumLocals(); ++i) {
setFinder.list.push_back(nullptr);
}
LocalGraph::GetSetses getSetses;
LocalGraph::Locations locations;
FinitePowersetLattice<LocalSet*> lattice(std::move(setFinder.list));
ReachingDefinitionsTransferFunction transferFunction(lattice,
func->getNumLocals(), getSetses, locations);
MonotoneCFGAnalyzer<FinitePowersetLattice<LocalSet*>,
ReachingDefinitionsTransferFunction>
analyzer(lattice, transferFunction, cfg);
analyzer.evaluateFunctionEntry(func);
analyzer.evaluate();
FindAll<LocalSet> foundSets(func->body);
FindAll<LocalGet> foundGets(func->body);
transferFunction.beginResultCollection();
analyzer.collectResults();
transferFunction.endResultCollection();
LocalGraph::GetSetses expectedResult;
expectedResult[foundGets.list[0]].insert(foundSets.list[0]);
expectedResult[foundGets.list[1]].insert(foundSets.list[0]);
expectedResult[foundGets.list[2]].insert(nullptr);
EXPECT_EQ(expectedResult, getSetses);
}
TEST_F(CFGTest, ReachingDefinitionsIf) {
auto moduleText = R"wasm(
(module
(func $bar
(local $a (i32))
(local $b (i32))
(local.set $a
(i32.const 1)
)
(if
(i32.eq
(local.get $a)
(i32.const 2)
)
(local.set $b
(i32.const 3)
)
(local.set $a
(i32.const 4)
)
)
(drop
(local.get $b)
)
(drop
(local.get $a)
)
)
)
)wasm";
Module wasm;
parseWast(wasm, moduleText);
Function* func = wasm.getFunction("bar");
CFG cfg = CFG::fromFunction(func);
FindAll<LocalSet> setFinder(func->body);
for (size_t i = 0; i < func->getNumLocals(); ++i) {
setFinder.list.push_back(nullptr);
}
LocalGraph::GetSetses getSetses;
LocalGraph::Locations locations;
FinitePowersetLattice<LocalSet*> lattice(std::move(setFinder.list));
ReachingDefinitionsTransferFunction transferFunction(lattice,
func->getNumLocals(), getSetses, locations);
MonotoneCFGAnalyzer<FinitePowersetLattice<LocalSet*>,
ReachingDefinitionsTransferFunction>
analyzer(lattice, transferFunction, cfg);
analyzer.evaluateFunctionEntry(func);
analyzer.evaluate();
FindAll<LocalSet> foundSets(func->body);
FindAll<LocalGet> foundGets(func->body);
transferFunction.beginResultCollection();
analyzer.collectResults();
transferFunction.endResultCollection();
LocalGraph::GetSetses expectedResult;
expectedResult[foundGets.list[0]].insert(foundSets.list[0]);
expectedResult[foundGets.list[1]].insert(nullptr);
expectedResult[foundGets.list[1]].insert(foundSets.list[1]);
expectedResult[foundGets.list[2]].insert(foundSets.list[0]);
expectedResult[foundGets.list[2]].insert(foundSets.list[2]);
EXPECT_EQ(expectedResult, getSetses);
}
TEST_F(CFGTest, ReachingDefinitionsLoop) {
auto moduleText = R"wasm(
(module
(func $bar (param $a (i32)) (param $b (i32))
(loop $loop
(drop
(local.get $a)
)
(local.set $a
(i32.add
(i32.const 1)
(local.get $a)
)
)
(br_if $loop
(i32.le_u
(local.get $a)
(i32.const 7)
)
)
)
(local.set $b
(i32.sub
(local.get $b)
(local.get $a)
)
)
)
)
)wasm";
Module wasm;
parseWast(wasm, moduleText);
Function* func = wasm.getFunction("bar");
CFG cfg = CFG::fromFunction(func);
FindAll<LocalSet> setFinder(func->body);
for (size_t i = 0; i < func->getNumLocals(); ++i) {
setFinder.list.push_back(nullptr);
}
LocalGraph::GetSetses getSetses;
LocalGraph::Locations locations;
FinitePowersetLattice<LocalSet*> lattice(std::move(setFinder.list));
ReachingDefinitionsTransferFunction transferFunction(lattice,
func->getNumLocals(), getSetses, locations);
MonotoneCFGAnalyzer<FinitePowersetLattice<LocalSet*>,
ReachingDefinitionsTransferFunction>
analyzer(lattice, transferFunction, cfg);
analyzer.evaluateFunctionEntry(func);
analyzer.evaluate();
FindAll<LocalSet> foundSets(func->body);
FindAll<LocalGet> foundGets(func->body);
transferFunction.beginResultCollection();
analyzer.collectResults();
transferFunction.endResultCollection();
LocalGraph::GetSetses expectedResult;
expectedResult[foundGets.list[0]].insert(nullptr);
expectedResult[foundGets.list[0]].insert(foundSets.list[0]);
expectedResult[foundGets.list[1]].insert(nullptr);
expectedResult[foundGets.list[1]].insert(foundSets.list[0]);
expectedResult[foundGets.list[2]].insert(foundSets.list[0]);
expectedResult[foundGets.list[3]].insert(nullptr);
expectedResult[foundGets.list[4]].insert(foundSets.list[0]);
EXPECT_EQ(expectedResult, getSetses);
}