Compare commits
5 Commits
ed84a65757
...
5efcf1b9b0
| Author | SHA1 | Date | |
|---|---|---|---|
|
5efcf1b9b0
|
|||
|
136663aeab
|
|||
|
56ea4ba2ab
|
|||
|
dab7f73216
|
|||
|
1d4e8bb1d0
|
@@ -4,16 +4,16 @@ set -e
|
|||||||
|
|
||||||
BUILD_DIR="build"
|
BUILD_DIR="build"
|
||||||
|
|
||||||
echo "Running clang-format..."
|
FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(cpp|hpp)$' | grep -v 'deps/' || true)
|
||||||
|
|
||||||
FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(cpp|hpp)$' || true)
|
|
||||||
|
|
||||||
if [ -z "$FILES" ]; then
|
if [ -z "$FILES" ]; then
|
||||||
echo "No C/C++ files to check"
|
echo "No C/C++ files to check"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
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
|
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..."
|
echo "Running clang-tidy..."
|
||||||
@@ -21,7 +21,8 @@ echo "Running clang-tidy..."
|
|||||||
for file in $FILES; do
|
for file in $FILES; do
|
||||||
clang-tidy \
|
clang-tidy \
|
||||||
"$file" \
|
"$file" \
|
||||||
|
--header-filter="^(?!.*deps/).*" \
|
||||||
-p "$BUILD_DIR"
|
-p "$BUILD_DIR"
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "pre-commit checks passed"
|
echo "pre-commit checks passed"
|
||||||
|
|||||||
@@ -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,
|
Integer = 0,
|
||||||
Register,
|
Register,
|
||||||
Variable,
|
Variable,
|
||||||
|
Global,
|
||||||
Function,
|
Function,
|
||||||
Block,
|
Block,
|
||||||
BlockPair,
|
BlockPair,
|
||||||
@@ -34,6 +35,7 @@ struct ir_operand {
|
|||||||
std::uint64_t ver : 10;
|
std::uint64_t ver : 10;
|
||||||
} reg;
|
} reg;
|
||||||
std::uint16_t variable;
|
std::uint16_t variable;
|
||||||
|
std::uint16_t global;
|
||||||
std::uint64_t function;
|
std::uint64_t function;
|
||||||
std::uint64_t block;
|
std::uint64_t block;
|
||||||
struct block_pair_s {
|
struct block_pair_s {
|
||||||
@@ -172,7 +174,7 @@ struct ir_module_variable : ir_variable {
|
|||||||
|
|
||||||
std::uint16_t name;
|
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 {
|
struct ir_function_variable : ir_variable {
|
||||||
@@ -181,7 +183,7 @@ struct ir_function_variable : ir_variable {
|
|||||||
|
|
||||||
std::uint64_t name;
|
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 {
|
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_SRCS "src/**.cpp")
|
||||||
file(GLOB_RECURSE FURDB_HDRS "include/**.hpp")
|
file(GLOB_RECURSE FURDB_HDRS "include/**.hpp")
|
||||||
add_executable(furdb ${FURDB_SRCS} ${FURDB_HDRS})
|
add_executable(furdb ${FURDB_SRCS} ${FURDB_HDRS})
|
||||||
target_include_directories(furdb PRIVATE include/)
|
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()) {
|
if (info.args.empty()) {
|
||||||
std::cout << "Possible arguments:\n";
|
std::cout << "Possible arguments:\n";
|
||||||
std::cout << "- variables\n";
|
std::cout << "- variables\n";
|
||||||
|
std::cout << "- stack\n";
|
||||||
} else if (info.args[0] == "variables") {
|
} else if (info.args[0] == "variables") {
|
||||||
if (ctx.executor->frames().empty()) {
|
if (ctx.executor->done()) {
|
||||||
std::cerr << "Not running\n";
|
std::cerr << "Not running\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -185,6 +186,18 @@ void info_command::execute(context& ctx, const command_info& info) {
|
|||||||
print_thing(frame.variables[i]);
|
print_thing(frame.variables[i]);
|
||||||
std::cout << '\n';
|
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 {
|
} else {
|
||||||
std::cerr << "Unexpected argument \"" << info.args[0] << "\"\n";
|
std::cerr << "Unexpected argument \"" << info.args[0] << "\"\n";
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-2
@@ -1,6 +1,7 @@
|
|||||||
#include "command.hpp"
|
#include "command.hpp"
|
||||||
#include "context.hpp"
|
#include "context.hpp"
|
||||||
#include "furvm/function.hpp"
|
#include "furvm/function.hpp"
|
||||||
|
#include "isocline.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <furvm/fwd.hpp>
|
#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["break"] = s_commands["b"] = new break_command();
|
||||||
s_commands["info"] = s_commands["i"] = new info_command();
|
s_commands["info"] = s_commands["i"] = new info_command();
|
||||||
|
|
||||||
|
ic_set_history(nullptr, -1);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::ifstream file(argv[1], std::ios::binary | std::ios::in);
|
std::ifstream file(argv[1], std::ios::binary | std::ios::in);
|
||||||
|
|
||||||
@@ -72,8 +75,11 @@ int main(int argc, char** argv) {
|
|||||||
std::string prevInput;
|
std::string prevInput;
|
||||||
while (ctx.running) {
|
while (ctx.running) {
|
||||||
std::swap(inputLine, prevInput);
|
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()) {
|
if (inputLine.empty()) {
|
||||||
inputLine = prevInput;
|
inputLine = prevInput;
|
||||||
|
|||||||
@@ -150,6 +150,8 @@ public:
|
|||||||
thing<>& top_thing();
|
thing<>& top_thing();
|
||||||
|
|
||||||
const thing<>& top_thing() const;
|
const thing<>& top_thing() const;
|
||||||
|
|
||||||
|
const std::vector<thing<>>& stack() const { return m_stack; }
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Stores a thing in a frame variable.
|
* @brief Stores a thing in a frame variable.
|
||||||
@@ -191,8 +193,8 @@ private:
|
|||||||
executor_flags m_flags = executor_flags::Done;
|
executor_flags m_flags = executor_flags::Done;
|
||||||
context* m_context;
|
context* m_context;
|
||||||
|
|
||||||
std::stack<frame> m_frames;
|
std::stack<frame> m_frames;
|
||||||
std::stack<thing<>> m_stack;
|
std::vector<thing<>> m_stack;
|
||||||
|
|
||||||
new_frame_callback m_newFrameCb = nullptr;
|
new_frame_callback m_newFrameCb = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -140,28 +140,28 @@ struct executor::frame executor::top_frame() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
thing<>& executor::push_thing(thing<>&& thing) {
|
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) {
|
thing<>& executor::push_thing(const thing<>& thing) {
|
||||||
return m_stack.emplace(thing);
|
return m_stack.emplace_back(thing);
|
||||||
}
|
}
|
||||||
|
|
||||||
thing<> executor::pop_thing() {
|
thing<> executor::pop_thing() {
|
||||||
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
|
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
|
||||||
auto top = std::move(m_stack.top());
|
auto top = std::move(m_stack.back());
|
||||||
m_stack.pop();
|
m_stack.pop_back();
|
||||||
return std::move(top);
|
return std::move(top);
|
||||||
}
|
}
|
||||||
|
|
||||||
thing<>& executor::top_thing() {
|
thing<>& executor::top_thing() {
|
||||||
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
|
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 {
|
const thing<>& executor::top_thing() const {
|
||||||
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
|
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) {
|
void executor::store_thing(variable_t variable, const thing<>& thing) {
|
||||||
|
|||||||
Reference in New Issue
Block a user