Skip to content

Commit 03878f7

Browse files
committed
Implement arena allocator to reduce allocation
In this patch, by introducing arena allocator, also known as regional allocator, and optimizing calloc byte buffer clearing algorithm, the compiler's memory usage, page fault occurrence frequency, and performance are significantly improved. The resident set size has been reduced up to 2/3. Arena allocator's introduction allows future development safely allocating structures without worrying their lifetime. This has already been implemented in SSA unit's "basic_block_t" allocation, which now doesn't require traversal to free up all connected detached basic blocks. In addition, calloc's byte buffer clearing algorithm has been changed into bulk clearing algorithm, which clears up 4 bytes at a time when requested byte buffer's size is large enough.
1 parent 6196ab1 commit 03878f7

File tree

12 files changed

+183
-74
lines changed

12 files changed

+183
-74
lines changed

lib/c.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -629,12 +629,27 @@ void *malloc(int size)
629629

630630
void *calloc(int n, int size)
631631
{
632-
char *p = malloc(n * size);
632+
int total = n * size;
633+
char *p = malloc(total);
633634

634635
if (!p)
635636
return NULL;
636-
for (int i = 0; i < n * size; i++)
637-
p[i] = 0;
637+
638+
/* TODO: Replace the byte buffer clearing algorithm with memset once
639+
* implemented.
640+
*/
641+
642+
/* Currently malloc uses mmap(2) to request allocation, which guarantees
643+
* memory to be page-aligned
644+
*/
645+
int *pi = p, num_words = total >> 2, offset = num_words << 2;
646+
647+
for (int i = 0; i < num_words; i++)
648+
pi[i] = 0;
649+
650+
while (offset < total)
651+
p[offset++] = 0;
652+
638653
return p;
639654
}
640655

src/arm-codegen.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,7 @@ void cfg_flatten()
156156

157157
for (ph2_ir_t *insn = bb->ph2_ir_list.head; insn;
158158
insn = insn->next) {
159-
flatten_ir = add_ph2_ir(OP_generic);
160-
memcpy(flatten_ir, insn, sizeof(ph2_ir_t));
159+
flatten_ir = add_existed_ph2_ir(insn);
161160

162161
if (insn->op == OP_return) {
163162
/* restore sp */
@@ -470,7 +469,7 @@ void code_generate()
470469
emit(__b(__AL, MAIN_BB->elf_offset - elf_code_idx));
471470

472471
for (int i = 0; i < ph2_ir_idx; i++) {
473-
ph2_ir = &PH2_IR[i];
472+
ph2_ir = PH2_IR_FLATTEN[i];
474473
emit_ph2_ir(ph2_ir);
475474
}
476475
}

src/defs.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@
4141
#define MAX_OPERAND_STACK_SIZE 32
4242
#define MAX_ANALYSIS_STACK_SIZE 750
4343

44+
/* Default capacities for common data structures */
45+
/* Default arena size is initialized with 256 KiB */
46+
#define DEFAULT_ARENA_SIZE 262144
47+
4448
#define ELF_START 0x10000
4549
#define PTR_SIZE 4
4650

@@ -62,6 +66,18 @@
6266
#define HOST_PTR_SIZE __SIZEOF_POINTER__
6367
#endif
6468

69+
/* Common data structures */
70+
typedef struct arena_block {
71+
char *memory;
72+
int capacity;
73+
int offset;
74+
struct arena_block *next;
75+
} arena_block_t;
76+
77+
typedef struct {
78+
arena_block_t *head;
79+
} arena_t;
80+
6581
/* builtin types */
6682
typedef enum {
6783
TYPE_void = 0,

src/globals.c

Lines changed: 137 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,12 @@ int global_ir_idx = 0;
3232
ph1_ir_t *PH1_IR;
3333
int ph1_ir_idx = 0;
3434

35-
ph2_ir_t *PH2_IR;
35+
arena_t *INSN_ARENA;
36+
37+
/* BB_ARENA is responsible for basic_block_t / ph2_ir_t allocation */
38+
arena_t *BB_ARENA;
39+
40+
ph2_ir_t **PH2_IR_FLATTEN;
3641
int ph2_ir_idx = 0;
3742

3843
label_lut_t *LABEL_LUT;
@@ -69,6 +74,121 @@ char *elf_symtab;
6974
char *elf_strtab;
7075
char *elf_section;
7176

77+
/**
78+
* arena_block_create() - creates a new arena block with given capacity.
79+
* The created arena block is guaranteed to be zero-initialized.
80+
* @capacity: The capacity of the arena block. Must be positive.
81+
*
82+
* Return: The pointer of created arena block. NULL if failed to allocate.
83+
*/
84+
arena_block_t *arena_block_create(int capacity)
85+
{
86+
arena_block_t *block = malloc(sizeof(arena_block_t));
87+
88+
if (!block) {
89+
printf("Failed to allocate memory for arena block\n");
90+
exit(1);
91+
}
92+
93+
block->memory = calloc(capacity, sizeof(char));
94+
95+
if (!block->memory) {
96+
printf("Failed to allocate memory for arena block\n");
97+
free(block);
98+
exit(1);
99+
}
100+
101+
block->capacity = capacity;
102+
block->offset = 0;
103+
block->next = NULL;
104+
return block;
105+
}
106+
107+
/**
108+
* arena_init() - initializes the given arena with initial capacity.
109+
* @initial_capacity: The initial capacity of the arena. Must be positive.
110+
*
111+
* Return: The pointer of initialized arena.
112+
*/
113+
arena_t *arena_init(int initial_capacity)
114+
{
115+
arena_t *arena = malloc(sizeof(arena_t));
116+
arena->head = arena_block_create(initial_capacity);
117+
return arena;
118+
}
119+
120+
/**
121+
* arena_alloc() - allocates memory from the given arena with given size.
122+
* The arena may create a new arena block if no space is available.
123+
* @arena: The arena to allocate memory from. Must not be NULL.
124+
* @size: The size of memory to allocate. Must be positive.
125+
*
126+
* Return: The pointer of allocated memory. NULL if new arena block is failed to
127+
* allocate.
128+
*/
129+
void *arena_alloc(arena_t *arena, int size)
130+
{
131+
char *ptr;
132+
arena_block_t *block = arena->head;
133+
134+
while (block) {
135+
if (block->offset + size <= block->capacity) {
136+
ptr = block->memory + block->offset;
137+
block->offset += size;
138+
return ptr;
139+
}
140+
if (!block->next)
141+
break;
142+
block = block->next;
143+
}
144+
145+
/* If no space is available, create a new block
146+
* Allocate at least 256 KiB or the requested size
147+
*/
148+
int new_capacity = size > DEFAULT_ARENA_SIZE ? size : DEFAULT_ARENA_SIZE;
149+
arena_block_t *new_block = arena_block_create(new_capacity);
150+
151+
if (!new_block)
152+
return NULL;
153+
154+
block->next = new_block;
155+
ptr = new_block->memory + new_block->offset;
156+
new_block->offset += size;
157+
return ptr;
158+
}
159+
160+
/**
161+
* arena_reset() - resets the given arena by resetting all blocks' offset to 0.
162+
* @arena: The arena to reset. Must not be NULL.
163+
*/
164+
void arena_reset(arena_t *arena)
165+
{
166+
arena_block_t *block = arena->head;
167+
168+
while (block) {
169+
block->offset = 0;
170+
block = block->next;
171+
}
172+
}
173+
174+
/**
175+
* arena_free() - frees the given arena and all its blocks.
176+
* @arena: The arena to free. Must not be NULL.
177+
*/
178+
void arena_free(arena_t *arena)
179+
{
180+
arena_block_t *block = arena->head, *next;
181+
182+
while (block) {
183+
next = block->next;
184+
free(block->memory);
185+
free(block);
186+
block = next;
187+
}
188+
189+
free(arena);
190+
}
191+
72192
/**
73193
* hashmap_hash_index() - hashses a string with FNV-1a hash function
74194
* and converts into usable hashmap index. The range of returned
@@ -312,11 +432,17 @@ ph1_ir_t *add_ph1_ir(opcode_t op)
312432
return ph1_ir;
313433
}
314434

435+
ph2_ir_t *add_existed_ph2_ir(ph2_ir_t *ph2_ir)
436+
{
437+
PH2_IR_FLATTEN[ph2_ir_idx++] = ph2_ir;
438+
return ph2_ir;
439+
}
440+
315441
ph2_ir_t *add_ph2_ir(opcode_t op)
316442
{
317-
ph2_ir_t *ph2_ir = &PH2_IR[ph2_ir_idx++];
443+
ph2_ir_t *ph2_ir = arena_alloc(BB_ARENA, sizeof(ph2_ir_t));
318444
ph2_ir->op = op;
319-
return ph2_ir;
445+
return add_existed_ph2_ir(ph2_ir);
320446
}
321447

322448
void set_var_liveout(var_t *var, int end)
@@ -579,7 +705,7 @@ fn_t *add_fn()
579705
/* Create a basic block and set the scope of variables to 'parent' block */
580706
basic_block_t *bb_create(block_t *parent)
581707
{
582-
basic_block_t *bb = calloc(1, sizeof(basic_block_t));
708+
basic_block_t *bb = arena_alloc(BB_ARENA, sizeof(basic_block_t));
583709

584710
for (int i = 0; i < MAX_BB_PRED; i++) {
585711
bb->prev[i].bb = NULL;
@@ -691,7 +817,7 @@ void add_insn(block_t *block,
691817

692818
bb->scope = block;
693819

694-
insn_t *n = calloc(1, sizeof(insn_t));
820+
insn_t *n = arena_alloc(INSN_ARENA, sizeof(insn_t));
695821
n->opcode = op;
696822
n->rd = rd;
697823
n->rs1 = rs1;
@@ -725,7 +851,9 @@ void global_init()
725851
TYPES = malloc(MAX_TYPES * sizeof(type_t));
726852
GLOBAL_IR = malloc(MAX_GLOBAL_IR * sizeof(ph1_ir_t));
727853
PH1_IR = malloc(MAX_IR_INSTR * sizeof(ph1_ir_t));
728-
PH2_IR = malloc(MAX_IR_INSTR * sizeof(ph2_ir_t));
854+
INSN_ARENA = arena_init(DEFAULT_ARENA_SIZE);
855+
BB_ARENA = arena_init(DEFAULT_ARENA_SIZE);
856+
PH2_IR_FLATTEN = malloc(MAX_IR_INSTR * sizeof(ph2_ir_t *));
729857
LABEL_LUT = malloc(MAX_LABEL * sizeof(label_lut_t));
730858
SOURCE = malloc(MAX_SOURCE);
731859
ALIASES = malloc(MAX_ALIASES * sizeof(alias_t));
@@ -755,7 +883,9 @@ void global_release()
755883
free(TYPES);
756884
free(GLOBAL_IR);
757885
free(PH1_IR);
758-
free(PH2_IR);
886+
arena_free(INSN_ARENA);
887+
arena_free(BB_ARENA);
888+
free(PH2_IR_FLATTEN);
759889
free(LABEL_LUT);
760890
free(SOURCE);
761891
free(ALIASES);

src/main.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ int main(int argc, char *argv[])
115115
elf_generate(out);
116116

117117
/* release allocated objects */
118-
ssa_release();
119118
global_release();
120119

121120
exit(0);

src/reg-alloc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ void refresh(basic_block_t *bb, insn_t *insn)
4949

5050
ph2_ir_t *bb_add_ph2_ir(basic_block_t *bb, opcode_t op)
5151
{
52-
ph2_ir_t *n = calloc(1, sizeof(ph2_ir_t));
52+
ph2_ir_t *n = arena_alloc(BB_ARENA, sizeof(ph2_ir_t));
5353
n->op = op;
5454

5555
if (!bb->ph2_ir_list.head)
@@ -640,7 +640,7 @@ void reg_alloc()
640640
void dump_ph2_ir()
641641
{
642642
for (int i = 0; i < ph2_ir_idx; i++) {
643-
ph2_ir_t *ph2_ir = &PH2_IR[i];
643+
ph2_ir_t *ph2_ir = PH2_IR_FLATTEN[i];
644644

645645
int rd = ph2_ir->dest + 48;
646646
int rs1 = ph2_ir->src0 + 48;

src/riscv-codegen.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,7 @@ void cfg_flatten()
128128

129129
for (ph2_ir_t *insn = bb->ph2_ir_list.head; insn;
130130
insn = insn->next) {
131-
flatten_ir = add_ph2_ir(OP_generic);
132-
memcpy(flatten_ir, insn, sizeof(ph2_ir_t));
131+
flatten_ir = add_existed_ph2_ir(insn);
133132

134133
if (insn->op == OP_return) {
135134
/* restore sp */
@@ -446,7 +445,7 @@ void code_generate()
446445
emit(__jal(__zero, MAIN_BB->elf_offset - elf_code_idx));
447446

448447
for (int i = 0; i < ph2_ir_idx; i++) {
449-
ph2_ir = &PH2_IR[i];
448+
ph2_ir = PH2_IR_FLATTEN[i];
450449
emit_ph2_ir(ph2_ir);
451450
}
452451
}

src/ssa.c

Lines changed: 2 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ bool insert_phi_insn(basic_block_t *bb, var_t *var)
588588
return false;
589589

590590
insn_t *head = bb->insn_list.head;
591-
insn_t *n = calloc(1, sizeof(insn_t));
591+
insn_t *n = arena_alloc(INSN_ARENA, sizeof(insn_t));
592592
n->opcode = OP_phi;
593593
n->rd = var;
594594
n->rs1 = var;
@@ -810,7 +810,7 @@ void solve_phi_params()
810810

811811
void append_unwound_phi_insn(basic_block_t *bb, var_t *dest, var_t *rs)
812812
{
813-
insn_t *n = calloc(1, sizeof(insn_t));
813+
insn_t *n = arena_alloc(INSN_ARENA, sizeof(insn_t));
814814
n->opcode = OP_unwound_phi;
815815
n->rd = dest;
816816
n->rs1 = rs;
@@ -1770,52 +1770,3 @@ void liveness_analysis()
17701770
} while (changed);
17711771
}
17721772
}
1773-
1774-
void bb_release(fn_t *fn, basic_block_t *bb)
1775-
{
1776-
UNUSED(fn);
1777-
1778-
insn_t *insn = bb->insn_list.head;
1779-
insn_t *next_insn;
1780-
while (insn) {
1781-
next_insn = insn->next;
1782-
free(insn);
1783-
insn = next_insn;
1784-
}
1785-
1786-
/* disconnect all predecessors */
1787-
for (int i = 0; i < MAX_BB_PRED; i++) {
1788-
if (!bb->prev[i].bb)
1789-
continue;
1790-
switch (bb->prev[i].type) {
1791-
case NEXT:
1792-
bb->prev[i].bb->next = NULL;
1793-
break;
1794-
case THEN:
1795-
bb->prev[i].bb->then_ = NULL;
1796-
break;
1797-
case ELSE:
1798-
bb->prev[i].bb->else_ = NULL;
1799-
break;
1800-
default:
1801-
abort();
1802-
}
1803-
1804-
bb->prev[i].bb = NULL;
1805-
}
1806-
free(bb);
1807-
}
1808-
1809-
void ssa_release()
1810-
{
1811-
bb_traversal_args_t *args = calloc(1, sizeof(bb_traversal_args_t));
1812-
for (fn_t *fn = FUNC_LIST.head; fn; fn = fn->next) {
1813-
args->fn = fn;
1814-
args->bb = fn->bbs;
1815-
1816-
fn->visited++;
1817-
args->postorder_cb = bb_release;
1818-
bb_forward_traversal(args);
1819-
}
1820-
free(args);
1821-
}

tests/snapshots/fib-arm.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

tests/snapshots/fib-riscv.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)