Skip to content

Commit 8ecc88b

Browse files
authored
Merge pull request #31 from AnyDSL/refactor/arena
Use an Arena for AST nodes
2 parents 0493761 + da8013e commit 8ecc88b

File tree

14 files changed

+273
-157
lines changed

14 files changed

+273
-157
lines changed

include/artic/arena.h

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#ifndef ARTIC_ARENA_H
2+
#define ARTIC_ARENA_H
3+
4+
#include <type_traits>
5+
#include <memory>
6+
#include <vector>
7+
#include <tuple>
8+
9+
template<typename T>
10+
/** works like unique_ptr but doesn't actually own anything */
11+
struct arena_ptr {
12+
T* _ptr;
13+
14+
arena_ptr() : _ptr(nullptr) {}
15+
arena_ptr(T* ptr) : _ptr(ptr) {}
16+
17+
// template<typename S, std::enable_if_t<std::is_convertible_v<S*, T*>, bool> = true>
18+
// arena_ptr(arena_ptr<S>& other) : _ptr(other._ptr) {}
19+
20+
template<typename S, std::enable_if_t<std::is_convertible_v<S*, T*>, bool> = true>
21+
arena_ptr(arena_ptr<S>&& other) : _ptr(other._ptr) {
22+
other._ptr = nullptr;
23+
}
24+
~arena_ptr() {
25+
_ptr = nullptr;
26+
}
27+
28+
// arena_ptr<T>& operator=(const arena_ptr<T>& other) { _ptr = other._ptr; return *this; }
29+
arena_ptr<T>& operator=(arena_ptr<T>&& other) { _ptr = other._ptr; other._ptr = nullptr; return *this; }
30+
31+
T* operator->() const { return _ptr; }
32+
T& operator*() const { return *_ptr; }
33+
operator bool() const { return _ptr; }
34+
35+
T* get() const { return _ptr; }
36+
37+
void swap(arena_ptr<T>& other) {
38+
T* tmp = other._ptr;
39+
other._ptr = _ptr;
40+
_ptr = tmp;
41+
}
42+
};
43+
44+
struct Arena {
45+
Arena();
46+
~Arena();
47+
48+
template<typename T, typename std::enable_if<std::is_trivially_destructible<T>::value, bool>::type = true, typename ...Args>
49+
arena_ptr<T> make_ptr(Args&& ...args) {
50+
void* ptr = alloc(sizeof(T));
51+
new (ptr) T (std::forward<Args>(args)...);
52+
return arena_ptr<T>(static_cast<T*>(ptr));
53+
}
54+
55+
template<typename T, typename std::enable_if<!std::is_trivially_destructible<T>::value, bool>::type = true, typename ...Args>
56+
arena_ptr<T> make_ptr(Args&& ...args) {
57+
void* ptr = alloc(sizeof(T));
58+
new (ptr) T (std::forward<Args>(args)...);
59+
auto deleter = [] (T* t) -> void {
60+
t->~T();
61+
};
62+
auto fn = static_cast<void(*)(T*)>(deleter);
63+
auto generic_fn = reinterpret_cast<void(*)(void*)>(fn);
64+
_cleanup.emplace_back(generic_fn, ptr);
65+
return arena_ptr<T>(static_cast<T*>(ptr));
66+
}
67+
private:
68+
void* alloc(size_t);
69+
void grow();
70+
71+
size_t _block_size;
72+
size_t _available;
73+
std::vector<void*> _data;
74+
std::vector<std::tuple<void (*)(void*), void*>> _cleanup;
75+
};
76+
77+
#endif // ARTIC_ARENA_H

include/artic/ast.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <vector>
66
#include <variant>
77

8+
#include "artic/arena.h"
89
#include "artic/loc.h"
910
#include "artic/log.h"
1011
#include "artic/cast.h"
@@ -25,12 +26,8 @@ class TypeChecker;
2526
class Emitter;
2627
class Summoner;
2728

28-
template <typename T> using Ptr = std::unique_ptr<T>;
29-
template <typename T> using PtrVector = std::vector<std::unique_ptr<T>>;
30-
template <typename T, typename... Args>
31-
std::unique_ptr<T> make_ptr(Args&&... args) {
32-
return std::make_unique<T>(std::forward<Args>(args)...);
33-
}
29+
template <typename T> using Ptr = arena_ptr<T>;
30+
template <typename T> using PtrVector = std::vector<Ptr<T>>;
3431

3532
namespace ast {
3633

@@ -155,7 +152,7 @@ struct Ptrn : public Node {
155152
/// Collect patterns that bind an identifier to a value in this pattern.
156153
virtual void collect_bound_ptrns(std::vector<const IdPtrn*>&) const;
157154
/// Rewrites the pattern into an expression
158-
virtual const Expr* to_expr() { return as_expr.get(); }
155+
virtual const Expr* to_expr(Arena&) { return as_expr.get(); }
159156
/// Returns true when the pattern is trivial (e.g. always matches).
160157
virtual bool is_trivial() const = 0;
161158
/// Emits IR for the pattern, given a value to bind it to.
@@ -1579,7 +1576,7 @@ struct TypedPtrn : public Ptrn {
15791576
void emit(Emitter&, const thorin::Def*) const override;
15801577
const artic::Type* infer(TypeChecker&) override;
15811578
void bind(NameBinder&) override;
1582-
const Expr* to_expr() override;
1579+
const Expr* to_expr(Arena&) override;
15831580
void resolve_summons(Summoner&) override;
15841581
void print(Printer&) const override;
15851582
};
@@ -1600,7 +1597,7 @@ struct IdPtrn : public Ptrn {
16001597
const artic::Type* infer(TypeChecker&) override;
16011598
const artic::Type* check(TypeChecker&, const artic::Type*) override;
16021599
void bind(NameBinder&) override;
1603-
const Expr* to_expr() override;
1600+
const Expr* to_expr(Arena&) override;
16041601
void resolve_summons(Summoner&) override;
16051602
void print(Printer&) const override;
16061603
};
@@ -1618,7 +1615,7 @@ struct LiteralPtrn : public Ptrn {
16181615
const artic::Type* infer(TypeChecker&) override;
16191616
const artic::Type* check(TypeChecker&, const artic::Type*) override;
16201617
void bind(NameBinder&) override;
1621-
const Expr* to_expr() override;
1618+
const Expr* to_expr(Arena&) override;
16221619
void resolve_summons(Summoner&) override {};
16231620
void print(Printer&) const override;
16241621
};

include/artic/check.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ namespace artic {
1414
/// Utility class to perform bidirectional type checking.
1515
class TypeChecker : public Logger {
1616
public:
17-
TypeChecker(Log& log, TypeTable& type_table)
18-
: Logger(log), type_table(type_table)
17+
TypeChecker(Log& log, TypeTable& type_table, Arena& arena)
18+
: Logger(log), type_table(type_table), _arena(arena)
1919
{}
2020

2121
TypeTable& type_table;
@@ -82,6 +82,7 @@ class TypeChecker : public Logger {
8282

8383
private:
8484
std::unordered_set<const ast::Decl*> decls_;
85+
Arena& _arena;
8586
};
8687

8788
} // namespace artic

include/artic/emit.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ struct StructType;
1919
/// Helper class for Thorin IR generation.
2020
class Emitter : public Logger {
2121
public:
22-
Emitter(Log& log, thorin::World& world)
23-
: Logger(log), world(world)
22+
Emitter(Log& log, thorin::World& world, Arena& arena)
23+
: Logger(log), world(world), arena(arena)
2424
{}
2525

2626
thorin::World& world;
27+
Arena& arena;
2728

2829
struct State {
2930
const thorin::Def* mem = nullptr;
@@ -147,12 +148,13 @@ class Emitter : public Logger {
147148

148149
/// Helper function to compile a set of files and generate an AST and a thorin module.
149150
/// Errors are reported in the log, and this function returns true on success.
150-
bool compile(
151+
std::tuple<Ptr<ast::ModDecl>, bool> compile(
151152
const std::vector<std::string>& file_names,
152153
const std::vector<std::string>& file_data,
153154
bool warns_as_errors,
154155
bool enable_all_warns,
155-
ast::ModDecl& program,
156+
Arena& arena,
157+
TypeTable& table,
156158
thorin::World& world,
157159
Log& log);
158160

include/artic/parser.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace artic {
1717
/// Generates an AST from a stream of tokens.
1818
class Parser : public Logger {
1919
public:
20-
Parser(Log& log, Lexer&);
20+
Parser(Log& log, Lexer&, Arena&);
2121

2222
/// Parses a program read from the Lexer object.
2323
/// Errors are reported by the Logger.
@@ -208,6 +208,7 @@ class Parser : public Logger {
208208
Token ahead_[max_ahead];
209209
Lexer& lexer_;
210210
Loc prev_;
211+
Arena& _arena;
211212
};
212213

213214
} // namespace artic

include/artic/summoner.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ namespace artic {
1111

1212
class Summoner : public Logger {
1313
public:
14-
Summoner(Log& log)
15-
: Logger(log)
14+
Summoner(Log& log, Arena& arena)
15+
: Logger(log), _arena(arena)
1616
{}
1717

1818
/// Eliminates all SummonExpr from the program
@@ -28,6 +28,8 @@ class Summoner : public Logger {
2828
bool error = false;
2929
std::vector<TypeMap<const ast::Expr*>> scopes;
3030

31+
Arena& _arena;
32+
3133
friend ast::SummonExpr;
3234
friend ast::ImplicitDecl;
3335
friend ast::ModDecl;

src/CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ add_library(libartic
1414
../include/artic/symbol.h
1515
../include/artic/token.h
1616
../include/artic/types.h
17+
arena.cpp
1718
ast.cpp
1819
bind.cpp
1920
check.cpp
@@ -25,7 +26,7 @@ add_library(libartic
2526
summoner.cpp
2627
types.cpp)
2728

28-
set_target_properties(libartic PROPERTIES PREFIX "" CXX_STANDARD 17)
29+
set_target_properties(libartic PROPERTIES PREFIX "" CXX_STANDARD 20)
2930

3031
target_link_libraries(libartic PUBLIC ${Thorin_LIBRARIES})
3132
target_include_directories(libartic PUBLIC ${Thorin_INCLUDE_DIRS} ../include)
@@ -35,7 +36,7 @@ if (${COLORIZE})
3536
endif()
3637

3738
add_executable(artic main.cpp)
38-
set_target_properties(artic PROPERTIES CXX_STANDARD 17)
39+
set_target_properties(artic PROPERTIES CXX_STANDARD 20)
3940
target_compile_definitions(artic PUBLIC -DARTIC_VERSION_MAJOR=${PROJECT_VERSION_MAJOR} -DARTIC_VERSION_MINOR=${PROJECT_VERSION_MINOR})
4041
target_link_libraries(artic PUBLIC libartic)
4142
if (Thorin_HAS_JSON_SUPPORT)

src/arena.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include "artic/arena.h"
2+
3+
#include <cstdlib>
4+
5+
Arena::Arena() : _block_size(4096) {
6+
_data = { malloc(_block_size) };
7+
_available = _block_size;
8+
}
9+
10+
Arena::~Arena() {
11+
for (auto [f, p] : _cleanup) {
12+
f(p);
13+
}
14+
for (auto& ptr : _data)
15+
free(ptr);
16+
}
17+
18+
void Arena::grow() {
19+
_block_size *= 2;
20+
_data.push_back( malloc(_block_size) );
21+
_available = _block_size;
22+
}
23+
24+
void* Arena::alloc(size_t size) {
25+
while (size > _available)
26+
grow();
27+
size_t ptr = reinterpret_cast<size_t>(_data.back()) + _block_size - _available;
28+
_available -= size;
29+
return reinterpret_cast<void*>(ptr);
30+
}

src/ast.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -619,10 +619,10 @@ bool TypedPtrn::is_trivial() const {
619619
return !ptrn || ptrn->is_trivial();
620620
}
621621

622-
const Expr* TypedPtrn::to_expr() {
622+
const Expr* TypedPtrn::to_expr(Arena& arena) {
623623
if (!ptrn)
624624
return nullptr;
625-
return ptrn->to_expr();
625+
return ptrn->to_expr(arena);
626626
}
627627

628628
void IdPtrn::collect_bound_ptrns(std::vector<const IdPtrn*>& bound_ptrns) const {
@@ -635,7 +635,7 @@ bool IdPtrn::is_trivial() const {
635635
return !sub_ptrn || sub_ptrn->is_trivial();
636636
}
637637

638-
const Expr* IdPtrn::to_expr() {
638+
const Expr* IdPtrn::to_expr(Arena& arena) {
639639
if (as_expr)
640640
return as_expr.get();
641641
Identifier id = decl->id;
@@ -644,18 +644,18 @@ const Expr* IdPtrn::to_expr() {
644644
Path path = Path(loc, std::move(elems));
645645
path.start_decl = decl.get();
646646
path.is_value = true;
647-
as_expr = make_ptr<PathExpr>(std::move(path));
647+
as_expr = arena.make_ptr<PathExpr>(std::move(path));
648648
return as_expr.get();
649649
}
650650

651651
bool LiteralPtrn::is_trivial() const {
652652
return false;
653653
}
654654

655-
const Expr* LiteralPtrn::to_expr() {
655+
const Expr* LiteralPtrn::to_expr(Arena& arena) {
656656
if (as_expr)
657657
return as_expr.get();
658-
as_expr = make_ptr<LiteralExpr>(loc, lit);
658+
as_expr = arena.make_ptr<LiteralExpr>(loc, lit);
659659
return as_expr.get();
660660
}
661661

src/check.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ inline std::pair<const Type*, const Type*> remove_ptr(const Type* type) {
150150
const Type* TypeChecker::deref(Ptr<ast::Expr>& expr) {
151151
auto [ref_type, type] = remove_ref(infer(*expr));
152152
if (ref_type)
153-
expr = make_ptr<ast::ImplicitCastExpr>(expr->loc, std::move(expr), type);
153+
expr = _arena.make_ptr<ast::ImplicitCastExpr>(expr->loc, std::move(expr), type);
154154
return type;
155155
}
156156

@@ -169,7 +169,7 @@ const Type* TypeChecker::coerce(Ptr<ast::Expr>& expr, const Type* expected) {
169169
if (auto implicit = expected->isa<ImplicitParamType>()) {
170170
// Only the empty tuple () can be coerced into a Summon[T]
171171
if (is_unit(expr)) {
172-
Ptr<ast::Expr> summoned = make_ptr<ast::SummonExpr>(expr->loc, Ptr<ast::Type>());
172+
Ptr<ast::Expr> summoned = _arena.make_ptr<ast::SummonExpr>(expr->loc, Ptr<ast::Type>());
173173
summoned->type = implicit->underlying;
174174
expr.swap(summoned);
175175
return implicit->underlying;
@@ -193,21 +193,21 @@ const Type* TypeChecker::coerce(Ptr<ast::Expr>& expr, const Type* expected) {
193193
}
194194

195195
if (auto implicit = tuple_t->args[i]->isa<ImplicitParamType>()) {
196-
Ptr<ast::Expr> summoned = make_ptr<ast::SummonExpr>(loc, Ptr<ast::Type>());
196+
Ptr<ast::Expr> summoned = _arena.make_ptr<ast::SummonExpr>(loc, Ptr<ast::Type>());
197197
summoned->type = implicit->underlying;
198198
args.push_back(std::move(summoned));
199199
continue;
200200
}
201201

202202
bad_arguments(loc, "non-implicit arguments", i, tuple_t->args.size());
203203
}
204-
expr = make_ptr<ast::TupleExpr>(loc, std::move(args));
204+
expr = _arena.make_ptr<ast::TupleExpr>(loc, std::move(args));
205205
}
206206

207207
auto type = expr->type ? expr->type : check(*expr, expected);
208208
if (type != expected) {
209209
if (type->subtype(expected)) {
210-
expr = make_ptr<ast::ImplicitCastExpr>(expr->loc, std::move(expr), expected);
210+
expr = _arena.make_ptr<ast::ImplicitCastExpr>(expr->loc, std::move(expr), expected);
211211
return expected;
212212
} else
213213
return incompatible_types(expr->loc, type, expected);

0 commit comments

Comments
 (0)