Compare commits
5 Commits
ed84a65757
...
5efcf1b9b0
| Author | SHA1 | Date | |
|---|---|---|---|
|
5efcf1b9b0
|
|||
|
136663aeab
|
|||
|
56ea4ba2ab
|
|||
|
dab7f73216
|
|||
|
1d4e8bb1d0
|
@@ -4,16 +4,16 @@ set -e
|
||||
|
||||
BUILD_DIR="build"
|
||||
|
||||
echo "Running clang-format..."
|
||||
|
||||
FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(cpp|hpp)$' || true)
|
||||
FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(cpp|hpp)$' | grep -v 'deps/' || true)
|
||||
|
||||
if [ -z "$FILES" ]; then
|
||||
echo "No C/C++ files to check"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git diff -U0 --cached | \
|
||||
echo "Running clang-format..."
|
||||
|
||||
git diff -U0 --cached -- $FILES | \
|
||||
python3 <(curl -s https://raw.githubusercontent.com/llvm/llvm-project/refs/heads/main/clang/tools/clang-format/clang-format-diff.py) -p1 -i
|
||||
|
||||
echo "Running clang-tidy..."
|
||||
@@ -21,6 +21,7 @@ echo "Running clang-tidy..."
|
||||
for file in $FILES; do
|
||||
clang-tidy \
|
||||
"$file" \
|
||||
--header-filter="^(?!.*deps/).*" \
|
||||
-p "$BUILD_DIR"
|
||||
done
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "furdb/deps/isocline"]
|
||||
path = furdb/deps/isocline
|
||||
url = https://github.com/daanx/isocline
|
||||
@@ -22,6 +22,7 @@ struct ir_operand {
|
||||
Integer = 0,
|
||||
Register,
|
||||
Variable,
|
||||
Global,
|
||||
Function,
|
||||
Block,
|
||||
BlockPair,
|
||||
@@ -34,6 +35,7 @@ struct ir_operand {
|
||||
std::uint64_t ver : 10;
|
||||
} reg;
|
||||
std::uint16_t variable;
|
||||
std::uint16_t global;
|
||||
std::uint64_t function;
|
||||
std::uint64_t block;
|
||||
struct block_pair_s {
|
||||
@@ -172,7 +174,7 @@ struct ir_module_variable : ir_variable {
|
||||
|
||||
std::uint16_t name;
|
||||
|
||||
ir_operand operand() const final { return { ir_operand::Variable, name }; }
|
||||
ir_operand operand() const final { return { ir_operand::Global, name }; }
|
||||
};
|
||||
|
||||
struct ir_function_variable : ir_variable {
|
||||
@@ -181,7 +183,7 @@ struct ir_function_variable : ir_variable {
|
||||
|
||||
std::uint64_t name;
|
||||
|
||||
ir_operand operand() const final { return { ir_operand::Register, name }; }
|
||||
ir_operand operand() const final { return { ir_operand::Variable, name }; }
|
||||
};
|
||||
|
||||
struct ir_scope {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef FURC_MIDDLE_SSA_HPP
|
||||
#define FURC_MIDDLE_SSA_HPP
|
||||
|
||||
#include "furc/middle/ir.hpp"
|
||||
|
||||
namespace furc {
|
||||
|
||||
class ssa {
|
||||
ssa() = delete;
|
||||
public:
|
||||
static void process(ir_module& mod);
|
||||
static void destruct(ir_module& mod);
|
||||
};
|
||||
|
||||
} // namespace furc
|
||||
|
||||
#endif // FURC_MIDDLE_SSA_HPP
|
||||
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* Sources:
|
||||
* - Practical Improvements to the Construction and Deconstruction of Static Single Assignment Form:
|
||||
* https://web.archive.org/web/20100607003509/http://www.cs.rice.edu/~harv/my_papers/ssa.pdf
|
||||
* - A Simple, Fast Dominance Algorithm:
|
||||
* https://www.researchgate.net/publication/2569680_A_Simple_Fast_Dominance_Algorithm
|
||||
*/
|
||||
|
||||
#include "furc/middle/ssa.hpp"
|
||||
|
||||
#include "furc/middle/ir.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace furc {
|
||||
|
||||
namespace {
|
||||
|
||||
struct block_info {
|
||||
std::size_t order = 0;
|
||||
|
||||
std::unordered_set<std::size_t> preds;
|
||||
std::unordered_set<std::size_t> sucs;
|
||||
std::size_t idom = 0;
|
||||
};
|
||||
|
||||
void rpo_dfs(std::unordered_set<std::size_t>& visited,
|
||||
std::vector<std::size_t>& order,
|
||||
std::size_t block,
|
||||
std::vector<block_info>& blocks) {
|
||||
visited.insert(block);
|
||||
for (auto succ : blocks[block].sucs) {
|
||||
if (visited.find(succ) != visited.end()) continue;
|
||||
rpo_dfs(visited, order, succ, blocks);
|
||||
}
|
||||
order.push_back(block);
|
||||
}
|
||||
|
||||
void compute_rpo(std::vector<block_info>& blocks, std::vector<std::size_t>& order) {
|
||||
std::unordered_set<std::size_t> visited;
|
||||
if (!blocks.empty()) rpo_dfs(visited, order, 0, blocks);
|
||||
std::reverse(order.begin(), order.begin());
|
||||
for (std::size_t i = 0; i < order.size(); ++i) {
|
||||
blocks[order[i]].order = i;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t intersect(std::vector<block_info>& blocks, std::size_t b1, std::size_t b2) {
|
||||
std::size_t finger1 = b1;
|
||||
std::size_t finger2 = b2;
|
||||
while (finger1 != finger2) {
|
||||
while (finger1 < finger2)
|
||||
finger1 = blocks[finger1].idom;
|
||||
while (finger2 < finger1)
|
||||
finger2 = blocks[finger2].idom;
|
||||
}
|
||||
return finger1;
|
||||
}
|
||||
|
||||
void process_function(ir_function& func) {
|
||||
std::vector<block_info> blocks(func.blocks.size());
|
||||
|
||||
// 1. Compute CFG
|
||||
for (std::size_t i = 0; i < func.blocks.size(); ++i) {
|
||||
const auto& block = func.blocks[i];
|
||||
if (block.instructions.empty()) continue;
|
||||
|
||||
const auto& termInstr = block.instructions.back();
|
||||
switch (termInstr.type) {
|
||||
case ir_instruction::Branch: {
|
||||
const auto& src = termInstr.sources.front();
|
||||
if (src.type != ir_operand::Block) throw std::runtime_error("invalid operand");
|
||||
blocks[src.value.block].preds.insert(i);
|
||||
blocks[i].sucs.insert(src.value.block);
|
||||
} break;
|
||||
case ir_instruction::BranchCond: {
|
||||
const auto& src = termInstr.sources.front();
|
||||
if (src.type != ir_operand::Block) throw std::runtime_error("invalid operand");
|
||||
blocks[src.value.blockPair.first].preds.insert(i);
|
||||
blocks[src.value.blockPair.second].preds.insert(i);
|
||||
blocks[i].preds.insert(src.value.blockPair.first);
|
||||
blocks[i].preds.insert(src.value.blockPair.second);
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Computing dominance tree
|
||||
std::vector<std::size_t> order;
|
||||
order.reserve(blocks.size());
|
||||
compute_rpo(blocks, order);
|
||||
|
||||
blocks[order.front()].idom = order.front();
|
||||
|
||||
bool changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
|
||||
for (std::size_t i = 1; i < order.size(); ++i) {
|
||||
auto& block = blocks[order[i]];
|
||||
std::size_t newIdom = -1;
|
||||
bool found = false;
|
||||
for (auto pred : block.preds) {
|
||||
if (blocks[pred].idom == -1) continue;
|
||||
newIdom = found ? intersect(blocks, pred, newIdom) : pred;
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (block.idom != newIdom) {
|
||||
block.idom = newIdom;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ssa::process(ir_module& mod) {
|
||||
for (auto* func : mod.functions)
|
||||
process_function(*func);
|
||||
}
|
||||
|
||||
void ssa::destruct(ir_module& mod) {}
|
||||
|
||||
} // namespace furc
|
||||
@@ -1,5 +1,8 @@
|
||||
set(IC_USE_CXX ON)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/deps/isocline)
|
||||
|
||||
file(GLOB_RECURSE FURDB_SRCS "src/**.cpp")
|
||||
file(GLOB_RECURSE FURDB_HDRS "include/**.hpp")
|
||||
add_executable(furdb ${FURDB_SRCS} ${FURDB_HDRS})
|
||||
target_include_directories(furdb PRIVATE include/)
|
||||
target_link_libraries(furdb PUBLIC furlang libfurc libfurvm)
|
||||
target_link_libraries(furdb PUBLIC furlang libfurc libfurvm isocline)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Checks: '-*'
|
||||
Submodule
+1
Submodule furdb/deps/isocline added at 8d6dc1ef95
+14
-1
@@ -172,8 +172,9 @@ void info_command::execute(context& ctx, const command_info& info) {
|
||||
if (info.args.empty()) {
|
||||
std::cout << "Possible arguments:\n";
|
||||
std::cout << "- variables\n";
|
||||
std::cout << "- stack\n";
|
||||
} else if (info.args[0] == "variables") {
|
||||
if (ctx.executor->frames().empty()) {
|
||||
if (ctx.executor->done()) {
|
||||
std::cerr << "Not running\n";
|
||||
return;
|
||||
}
|
||||
@@ -185,6 +186,18 @@ void info_command::execute(context& ctx, const command_info& info) {
|
||||
print_thing(frame.variables[i]);
|
||||
std::cout << '\n';
|
||||
}
|
||||
} else if (info.args[0] == "stack") {
|
||||
if (ctx.executor->done()) {
|
||||
std::cerr << "Not running\n";
|
||||
return;
|
||||
}
|
||||
std::cout << "Stack from top to bottom:\n";
|
||||
const auto& frame = ctx.executor->top_frame();
|
||||
for (std::size_t begin = frame.stackBase, i = ctx.executor->stack().size(); i > begin;) {
|
||||
std::cout << "- ";
|
||||
print_thing(ctx.executor->stack()[--i]);
|
||||
std::cout << '\n';
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Unexpected argument \"" << info.args[0] << "\"\n";
|
||||
}
|
||||
|
||||
+8
-2
@@ -1,6 +1,7 @@
|
||||
#include "command.hpp"
|
||||
#include "context.hpp"
|
||||
#include "furvm/function.hpp"
|
||||
#include "isocline.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <furvm/fwd.hpp>
|
||||
@@ -50,6 +51,8 @@ int main(int argc, char** argv) {
|
||||
s_commands["break"] = s_commands["b"] = new break_command();
|
||||
s_commands["info"] = s_commands["i"] = new info_command();
|
||||
|
||||
ic_set_history(nullptr, -1);
|
||||
|
||||
try {
|
||||
std::ifstream file(argv[1], std::ios::binary | std::ios::in);
|
||||
|
||||
@@ -72,8 +75,11 @@ int main(int argc, char** argv) {
|
||||
std::string prevInput;
|
||||
while (ctx.running) {
|
||||
std::swap(inputLine, prevInput);
|
||||
std::cout << "(furdb) ";
|
||||
if (!std::getline(std::cin, inputLine)) break;
|
||||
|
||||
char* inputRaw = ic_readline("furdb");
|
||||
if (inputRaw == nullptr) break;
|
||||
inputLine = inputRaw;
|
||||
free(inputRaw); // NOLINT
|
||||
|
||||
if (inputLine.empty()) {
|
||||
inputLine = prevInput;
|
||||
|
||||
@@ -150,6 +150,8 @@ public:
|
||||
thing<>& top_thing();
|
||||
|
||||
const thing<>& top_thing() const;
|
||||
|
||||
const std::vector<thing<>>& stack() const { return m_stack; }
|
||||
public:
|
||||
/**
|
||||
* @brief Stores a thing in a frame variable.
|
||||
@@ -191,8 +193,8 @@ private:
|
||||
executor_flags m_flags = executor_flags::Done;
|
||||
context* m_context;
|
||||
|
||||
std::stack<frame> m_frames;
|
||||
std::stack<thing<>> m_stack;
|
||||
std::stack<frame> m_frames;
|
||||
std::vector<thing<>> m_stack;
|
||||
|
||||
new_frame_callback m_newFrameCb = nullptr;
|
||||
};
|
||||
|
||||
@@ -140,28 +140,28 @@ struct executor::frame executor::top_frame() const {
|
||||
}
|
||||
|
||||
thing<>& executor::push_thing(thing<>&& thing) {
|
||||
return m_stack.emplace(std::move(thing));
|
||||
return m_stack.emplace_back(std::move(thing));
|
||||
}
|
||||
|
||||
thing<>& executor::push_thing(const thing<>& thing) {
|
||||
return m_stack.emplace(thing);
|
||||
return m_stack.emplace_back(thing);
|
||||
}
|
||||
|
||||
thing<> executor::pop_thing() {
|
||||
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
|
||||
auto top = std::move(m_stack.top());
|
||||
m_stack.pop();
|
||||
auto top = std::move(m_stack.back());
|
||||
m_stack.pop_back();
|
||||
return std::move(top);
|
||||
}
|
||||
|
||||
thing<>& executor::top_thing() {
|
||||
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
|
||||
return m_stack.top();
|
||||
return m_stack.back();
|
||||
}
|
||||
|
||||
const thing<>& executor::top_thing() const {
|
||||
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
|
||||
return m_stack.top();
|
||||
return m_stack.back();
|
||||
}
|
||||
|
||||
void executor::store_thing(variable_t variable, const thing<>& thing) {
|
||||
|
||||
Reference in New Issue
Block a user