mirror of
https://github.com/sigmasternchen/macrofuck
synced 2025-03-15 07:08:56 +00:00
refactor: Refactor codegen into components
This commit is contained in:
parent
820c7c1a93
commit
cd6d710d13
5 changed files with 871 additions and 843 deletions
|
@ -12,8 +12,9 @@ MAKE = make
|
|||
PLUGINS =
|
||||
BUILTINS = obj/builtins/builtins.o obj/builtins/numbers.o \
|
||||
obj/builtins/print.o
|
||||
OBJS = obj/lex.yy.o obj/y.tab.o obj/codegen.o obj/ast.o obj/band.o \
|
||||
obj/plugins.o obj/scope.o $(PLUGINS) $(BUILTINS) obj/main.o
|
||||
OBJS = obj/lex.yy.o obj/y.tab.o obj/codegen.o obj/codegen_expr.o \
|
||||
obj/codegen_stat.o obj/ast.o obj/band.o obj/plugins.o obj/scope.o \
|
||||
$(PLUGINS) $(BUILTINS) obj/main.o
|
||||
DEPS = $(OBJS:%.o=%.d)
|
||||
|
||||
-include $(DEPS)
|
||||
|
|
|
@ -61,853 +61,12 @@ region_t* _clone(FILE* out, scope_t* scope, region_t* region) {
|
|||
return clone;
|
||||
}
|
||||
|
||||
|
||||
static void region_used(scope_t* scope, region_t* region) {
|
||||
if (region->is_temp) {
|
||||
scope_remove(scope, region);
|
||||
}
|
||||
}
|
||||
|
||||
void codegen_add_char(FILE* out, scope_t* scope, size_t position, char c) {
|
||||
move(position);
|
||||
reset();
|
||||
add(c);
|
||||
}
|
||||
|
||||
region_t* codegen_literal_expr(FILE* out, scope_t* scope, struct literal_expression expr) {
|
||||
region_t* region = NULL;
|
||||
switch(expr.kind) {
|
||||
case NUMBER_LITERAL:
|
||||
if (expr.number > 255) {
|
||||
fprintf(stderr, "literal %lld greater than 255\n", expr.number);
|
||||
panic("number literal too big");
|
||||
}
|
||||
region = scope_add_tmp(scope, 1);
|
||||
move_to(region);
|
||||
reset(); add(expr.number);
|
||||
break;
|
||||
case CHAR_LITERAL:
|
||||
region = scope_add_tmp(scope, 1);
|
||||
codegen_add_char(out, scope, region->start, expr.ch);
|
||||
break;
|
||||
case STRING_LITERAL: {
|
||||
size_t l = strlen(expr.str); // don't copy \0
|
||||
region = scope_add_tmp(scope, strlen(expr.str));
|
||||
for (size_t i = 0; i < l; i++) {
|
||||
codegen_add_char(out, scope, region->start + i, expr.str[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
fprintf(stderr, "literal kind: %d\n", expr.kind);
|
||||
panic("unknown literal kind");
|
||||
}
|
||||
return region;
|
||||
}
|
||||
|
||||
region_t* codegen_variable_expr(FILE* _, scope_t* scope, struct variable_expression expr) {
|
||||
region_t* region = scope_get(scope, expr.id);
|
||||
if (!region) {
|
||||
fprintf(stderr, "unknown variable: %s\n", expr.id);
|
||||
exit(1);
|
||||
}
|
||||
return region;
|
||||
}
|
||||
|
||||
region_t* codegen_macro_expr(FILE* out, scope_t* scope, struct macro_expression expr) {
|
||||
builtin_t macro = find_builtin(expr.id);
|
||||
if (macro == NULL) {
|
||||
panic("unable to find builtin");
|
||||
}
|
||||
|
||||
region_t* arg = scope_get(scope, expr.argument);
|
||||
if (arg == NULL) {
|
||||
panic("argument has to be a variable");
|
||||
}
|
||||
|
||||
return macro(out, scope, 1, &arg);
|
||||
}
|
||||
|
||||
region_t* codegen_expr(FILE*, scope_t*, struct expression*);
|
||||
|
||||
#define swap_or_clone(result, op1, op2, allow_swap) \
|
||||
if (op1->is_temp) { \
|
||||
result = op1; \
|
||||
} else if (op2->is_temp && allow_swap) { \
|
||||
result = op2; \
|
||||
op2 = op1; \
|
||||
} else { \
|
||||
result = clone(op1); \
|
||||
}
|
||||
|
||||
#define calc_prefix(allow_swap) \
|
||||
region_t* result; \
|
||||
swap_or_clone(result, op1, op2, allow_swap); \
|
||||
if (!op2->is_temp) { \
|
||||
op2 = clone(op2); \
|
||||
}
|
||||
|
||||
#define calc_postfix() \
|
||||
scope_remove(scope, op2); \
|
||||
return result;
|
||||
|
||||
region_t* codegen_addition(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
calc_prefix(true);
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
dec();
|
||||
move_to(result); inc();
|
||||
move_to(op2);
|
||||
});
|
||||
|
||||
calc_postfix();
|
||||
}
|
||||
region_t* codegen_subtraction(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
calc_prefix(false);
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
dec();
|
||||
move_to(result); dec();
|
||||
move_to(op2);
|
||||
});
|
||||
|
||||
calc_postfix();
|
||||
}
|
||||
region_t* codegen_multiplication(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
calc_prefix(true);
|
||||
|
||||
// make sure to not change op1
|
||||
op1 = clone(op1);
|
||||
region_t* tmp = clone(op1);
|
||||
|
||||
move_to(result); reset();
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
dec();
|
||||
|
||||
move_to(tmp);
|
||||
loop({
|
||||
dec();
|
||||
move_to(result); inc();
|
||||
move_to(tmp);
|
||||
});
|
||||
copy(op1, tmp);
|
||||
|
||||
move_to(op2);
|
||||
});
|
||||
|
||||
scope_remove(scope, tmp);
|
||||
scope_remove(scope, op1);
|
||||
|
||||
calc_postfix();
|
||||
}
|
||||
region_t* codegen_division(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
region_t* result = scope_add_tmp(scope, 1);
|
||||
move_to(result); reset();
|
||||
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
move_to(tmp); reset();
|
||||
copy(op2, tmp);
|
||||
|
||||
region_t* tmp2 = scope_add_tmp(scope, 1);
|
||||
|
||||
region_t* flag = scope_add_tmp(scope, 1);
|
||||
|
||||
move_to(op1);
|
||||
loop({
|
||||
dec();
|
||||
|
||||
move_to(tmp); dec();
|
||||
|
||||
move_to(flag); reset(); inc();
|
||||
|
||||
move_to(tmp2); reset();
|
||||
copy(tmp, tmp2);
|
||||
move_to(tmp2);
|
||||
loop({
|
||||
move_to(flag); reset();
|
||||
move_to(tmp2); reset();
|
||||
});
|
||||
|
||||
move_to(flag);
|
||||
loop({
|
||||
move_to(result); inc();
|
||||
copy(op2, tmp);
|
||||
move_to(flag); reset();
|
||||
});
|
||||
|
||||
move_to(op1);
|
||||
});
|
||||
|
||||
scope_remove(scope, flag);
|
||||
scope_remove(scope, tmp2);
|
||||
scope_remove(scope, tmp);
|
||||
scope_remove(scope, op1);
|
||||
scope_remove(scope, op2);
|
||||
return result;
|
||||
}
|
||||
region_t* codegen_modulo(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
move_to(tmp); reset();
|
||||
copy(op2, tmp);
|
||||
|
||||
region_t* tmp2 = scope_add_tmp(scope, 1);
|
||||
|
||||
region_t* flag = scope_add_tmp(scope, 1);
|
||||
|
||||
move_to(op1);
|
||||
loop({
|
||||
dec();
|
||||
|
||||
move_to(tmp); dec();
|
||||
|
||||
move_to(flag); reset(); inc();
|
||||
|
||||
move_to(tmp2); reset();
|
||||
copy(tmp, tmp2);
|
||||
move_to(tmp2);
|
||||
loop({
|
||||
move_to(flag); reset();
|
||||
move_to(tmp2); reset();
|
||||
});
|
||||
|
||||
move_to(flag);
|
||||
loop({
|
||||
copy(op2, tmp);
|
||||
move_to(flag); reset();
|
||||
});
|
||||
|
||||
move_to(op1);
|
||||
});
|
||||
|
||||
move_to(tmp);
|
||||
loop({
|
||||
loop({
|
||||
dec();
|
||||
move_to(op2); dec();
|
||||
move_to(tmp);
|
||||
});
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
dec();
|
||||
move_to(op1); inc();
|
||||
move_to(op2);
|
||||
});
|
||||
|
||||
move_to(tmp);
|
||||
});
|
||||
|
||||
scope_remove(scope, flag);
|
||||
scope_remove(scope, tmp2);
|
||||
scope_remove(scope, tmp);
|
||||
scope_remove(scope, op2);
|
||||
return op1;
|
||||
}
|
||||
|
||||
region_t* codegen_equals(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
dec();
|
||||
move_to(op1); dec();
|
||||
move_to(op2);
|
||||
});
|
||||
inc();
|
||||
|
||||
move_to(op1);
|
||||
loop({
|
||||
move_to(op2); dec();
|
||||
move_to(op1); reset();
|
||||
});
|
||||
|
||||
scope_remove(scope, op1);
|
||||
return op2;
|
||||
}
|
||||
|
||||
region_t* codegen_not_equals(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
dec();
|
||||
move_to(op1); dec();
|
||||
move_to(op2);
|
||||
});
|
||||
|
||||
scope_remove(scope, op2);
|
||||
return op1;
|
||||
}
|
||||
|
||||
region_t* codegen_conjunction(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
move_to(tmp); reset();
|
||||
|
||||
move_to(op1);
|
||||
loop({
|
||||
move_to(op2);
|
||||
loop({
|
||||
move_to(tmp); inc();
|
||||
move_to(op2); reset();
|
||||
});
|
||||
|
||||
move_to(op1); reset();
|
||||
});
|
||||
|
||||
scope_remove(scope, op1);
|
||||
scope_remove(scope, op2);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
region_t* codegen_disjunction(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
move_to(tmp); reset();
|
||||
|
||||
move_to(op1);
|
||||
loop({
|
||||
move_to(tmp); inc();
|
||||
move_to(op1); reset();
|
||||
});
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
move_to(tmp); inc();
|
||||
move_to(op2); reset();
|
||||
});
|
||||
|
||||
move_to(tmp);
|
||||
loop({
|
||||
move_to(op1); inc();
|
||||
move_to(tmp); reset();
|
||||
})
|
||||
|
||||
scope_remove(scope, op2);
|
||||
scope_remove(scope, tmp);
|
||||
|
||||
return op1;
|
||||
}
|
||||
|
||||
region_t* codegen_greater_than(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
region_t* result = scope_add_tmp(scope, 1);
|
||||
move_to(result); reset();
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
region_t* op2_is_empty = scope_add_tmp(scope, 1);
|
||||
|
||||
move_to(op1);
|
||||
loop({
|
||||
dec();
|
||||
move_to(op2_is_empty); reset(); inc();
|
||||
move_to(tmp); reset();
|
||||
move_to(op2);
|
||||
loop({
|
||||
move_to(tmp); inc();
|
||||
move_to(op2_is_empty); reset();
|
||||
move_to(op2); dec();
|
||||
});
|
||||
|
||||
move_to(tmp);
|
||||
loop({
|
||||
move_to(op2); inc();
|
||||
move_to(tmp); dec();
|
||||
});
|
||||
|
||||
move_to(op2); dec();
|
||||
|
||||
move_to(op2_is_empty);
|
||||
loop({
|
||||
move_to(result); inc();
|
||||
move_to(op1); reset();
|
||||
move_to(op2_is_empty); dec();
|
||||
});
|
||||
|
||||
move_to(op1);
|
||||
});
|
||||
|
||||
scope_remove(scope, op1);
|
||||
scope_remove(scope, op2);
|
||||
scope_remove(scope, tmp);
|
||||
scope_remove(scope, op2_is_empty);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
region_t* codegen_less_than(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
region_t* result = scope_add_tmp(scope, 1);
|
||||
move_to(result); reset();
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
region_t* op1_is_empty = scope_add_tmp(scope, 1);
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
dec();
|
||||
move_to(op1_is_empty); reset(); inc();
|
||||
move_to(tmp); reset();
|
||||
move_to(op1);
|
||||
loop({
|
||||
move_to(tmp); inc();
|
||||
move_to(op1_is_empty); reset();
|
||||
move_to(op1); dec();
|
||||
});
|
||||
|
||||
move_to(tmp);
|
||||
loop({
|
||||
move_to(op1); inc();
|
||||
move_to(tmp); dec();
|
||||
});
|
||||
|
||||
move_to(op1); dec();
|
||||
|
||||
move_to(op1_is_empty);
|
||||
loop({
|
||||
move_to(result); inc();
|
||||
move_to(op2); reset();
|
||||
move_to(op1_is_empty); dec();
|
||||
});
|
||||
|
||||
move_to(op2);
|
||||
});
|
||||
|
||||
scope_remove(scope, op1);
|
||||
scope_remove(scope, op2);
|
||||
scope_remove(scope, tmp);
|
||||
scope_remove(scope, op1_is_empty);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
region_t* codegen_greater_than_or_equals(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
region_t* result = scope_add_tmp(scope, 1);
|
||||
move_to(result); reset(); inc();
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
region_t* op1_is_empty = scope_add_tmp(scope, 1);
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
dec();
|
||||
move_to(op1_is_empty); reset(); inc();
|
||||
move_to(tmp); reset();
|
||||
move_to(op1);
|
||||
loop({
|
||||
move_to(tmp); inc();
|
||||
move_to(op1_is_empty); reset();
|
||||
move_to(op1); dec();
|
||||
});
|
||||
|
||||
move_to(tmp);
|
||||
loop({
|
||||
move_to(op1); inc();
|
||||
move_to(tmp); dec();
|
||||
});
|
||||
|
||||
move_to(op1); dec();
|
||||
|
||||
move_to(op1_is_empty);
|
||||
loop({
|
||||
move_to(result); reset();
|
||||
move_to(op2); reset();
|
||||
move_to(op1_is_empty); dec();
|
||||
});
|
||||
|
||||
move_to(op2);
|
||||
});
|
||||
|
||||
scope_remove(scope, op1);
|
||||
scope_remove(scope, op2);
|
||||
scope_remove(scope, tmp);
|
||||
scope_remove(scope, op1_is_empty);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
region_t* codegen_less_than_or_equals(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
region_t* result = scope_add_tmp(scope, 1);
|
||||
move_to(result); reset(); inc();
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
region_t* op2_is_empty = scope_add_tmp(scope, 1);
|
||||
|
||||
move_to(op1);
|
||||
loop({
|
||||
dec();
|
||||
move_to(op2_is_empty); reset(); inc();
|
||||
move_to(tmp); reset();
|
||||
move_to(op2);
|
||||
loop({
|
||||
move_to(tmp); inc();
|
||||
move_to(op2_is_empty); reset();
|
||||
move_to(op2); dec();
|
||||
});
|
||||
|
||||
move_to(tmp);
|
||||
loop({
|
||||
move_to(op2); inc();
|
||||
move_to(tmp); dec();
|
||||
});
|
||||
|
||||
move_to(op2); dec();
|
||||
|
||||
move_to(op2_is_empty);
|
||||
loop({
|
||||
move_to(result); reset();
|
||||
move_to(op1); reset();
|
||||
move_to(op2_is_empty); dec();
|
||||
});
|
||||
|
||||
move_to(op1);
|
||||
});
|
||||
|
||||
scope_remove(scope, op1);
|
||||
scope_remove(scope, op2);
|
||||
scope_remove(scope, tmp);
|
||||
scope_remove(scope, op2_is_empty);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
region_t* codegen_negation(FILE* out, scope_t* scope, region_t* op) {
|
||||
if (!op->is_temp) {
|
||||
op = clone(op);
|
||||
}
|
||||
|
||||
region_t* result = scope_add_tmp(scope, 1);
|
||||
move_to(result); reset(); inc();
|
||||
|
||||
move_to(op);
|
||||
loop({
|
||||
move_to(result); dec();
|
||||
move_to(op); reset();
|
||||
});
|
||||
|
||||
scope_remove(scope, op);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
region_t* codegen_calc_expr(FILE* out, scope_t* scope, struct calc_expression expr) {
|
||||
region_t* operand1 = codegen_expr(out, scope, expr.operand1);
|
||||
region_t* operand2 = NULL;
|
||||
if (expr.operand2) {
|
||||
operand2 = codegen_expr(out, scope, expr.operand2);
|
||||
}
|
||||
|
||||
region_t* result = NULL;
|
||||
|
||||
switch (expr.operator) {
|
||||
case ADDITION:
|
||||
result = codegen_addition(out, scope, operand1, operand2);
|
||||
break;
|
||||
case SUBTRACTION:
|
||||
result = codegen_subtraction(out, scope, operand1, operand2);
|
||||
break;
|
||||
case MULTIPLICATION:
|
||||
result = codegen_multiplication(out, scope, operand1, operand2);
|
||||
break;
|
||||
case DIVISION:
|
||||
result = codegen_division(out, scope, operand1, operand2);
|
||||
break;
|
||||
case MODULO:
|
||||
result = codegen_modulo(out, scope, operand1, operand2);
|
||||
break;
|
||||
case EQUALS:
|
||||
result = codegen_equals(out, scope, operand1, operand2);
|
||||
break;
|
||||
case GREATER_THAN:
|
||||
result = codegen_greater_than(out, scope, operand1, operand2);
|
||||
break;
|
||||
case LESS_THAN:
|
||||
result = codegen_less_than(out, scope, operand1, operand2);
|
||||
break;
|
||||
case GREATER_EQUALS:
|
||||
result = codegen_greater_than_or_equals(out, scope, operand1, operand2);
|
||||
break;
|
||||
case LESS_EQUALS:
|
||||
result = codegen_less_than_or_equals(out, scope, operand1, operand2);
|
||||
break;
|
||||
case NOT_EQUALS:
|
||||
result = codegen_not_equals(out, scope, operand1, operand2);
|
||||
break;
|
||||
case CONJUNCTION:
|
||||
result = codegen_conjunction(out, scope, operand1, operand2);
|
||||
break;
|
||||
case DISJUNCTION:
|
||||
result = codegen_disjunction(out, scope, operand1, operand2);
|
||||
break;
|
||||
case NEGATION:
|
||||
result = codegen_negation(out, scope, operand1);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown operator: %d\n", expr.operator);
|
||||
panic("unknown operator");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
region_t* codegen_builtin_expr(FILE* out, scope_t* scope, struct builtin_call_expression expr) {
|
||||
builtin_t builtin = find_builtin(expr.id);
|
||||
|
||||
region_t** args = alloca(expr.argument_number * sizeof(region_t*));
|
||||
|
||||
for (size_t i = 0; i < expr.argument_number; i++) {
|
||||
args[i] = codegen_expr(out, scope, expr.arguments[i]);
|
||||
}
|
||||
|
||||
return builtin(out, scope, expr.argument_number, args);
|
||||
}
|
||||
|
||||
region_t* codegen_expr(FILE* out, scope_t* scope, struct expression* expr) {
|
||||
switch(expr->kind) {
|
||||
case LITERAL:
|
||||
return codegen_literal_expr(out, scope, expr->literal);
|
||||
case VARIABLE:
|
||||
return codegen_variable_expr(out, scope, expr->variable);
|
||||
case MACRO:
|
||||
return codegen_macro_expr(out, scope, expr->macro);
|
||||
case BUILTIN_CALL:
|
||||
return codegen_builtin_expr(out, scope, expr->builtin_call);
|
||||
case CALCULATION:
|
||||
return codegen_calc_expr(out, scope, expr->calc);
|
||||
default:
|
||||
fprintf(stderr, "expression kind: %d\n", expr->kind);
|
||||
panic("unknown expression kind");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
region_t* clone_region(FILE* out, scope_t* scope, region_t* original) {
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
region_t* clone = scope_add_tmp(scope, original->size);
|
||||
|
||||
move_to(tmp); reset();
|
||||
for (size_t i = 0; i < original->size; i++) {
|
||||
move_offset(clone, i); reset();
|
||||
|
||||
move_offset(original, i);
|
||||
loop({
|
||||
dec();
|
||||
move_to(tmp);
|
||||
inc();
|
||||
move_offset(clone, i);
|
||||
inc();
|
||||
move_offset(original, i);
|
||||
});
|
||||
|
||||
move_to(tmp);
|
||||
loop({
|
||||
dec();
|
||||
move_offset(original, i);
|
||||
inc();
|
||||
move_to(tmp);
|
||||
});
|
||||
}
|
||||
|
||||
scope_remove(scope, tmp);
|
||||
return clone;
|
||||
}
|
||||
|
||||
void _reset_region(FILE* out, scope_t* scope, region_t* region) {
|
||||
for (size_t i = 0; i < region->size; i++) {
|
||||
move_offset(region, i); reset();
|
||||
}
|
||||
}
|
||||
|
||||
void codegen_assignment_statement(FILE* out, scope_t* scope, struct assignment_statement statement) {
|
||||
region_t* region = codegen_expr(out, scope, statement.value);
|
||||
region_t* var = scope_get(scope, statement.id);
|
||||
if (!var) {
|
||||
fprintf(stderr, "variable not found: %s\n", statement.id);
|
||||
panic("unknown variable");
|
||||
}
|
||||
|
||||
if (var->start != region->start) {
|
||||
reset_region(var);
|
||||
copy(region, var);
|
||||
}
|
||||
|
||||
region_used(scope, region);
|
||||
}
|
||||
|
||||
void codegen_decl_statement(FILE* out, scope_t* scope, struct assignment_statement statement) {
|
||||
if (scope_get(scope, statement.id)) {
|
||||
fprintf(stderr, "variable exists: %s\n", statement.id);
|
||||
panic("variable exists");
|
||||
}
|
||||
|
||||
region_t* region = codegen_expr(out, scope, statement.value);
|
||||
|
||||
if (!region->is_temp) {
|
||||
region = clone_region(out, scope, region);
|
||||
}
|
||||
|
||||
scope_existing(scope, region, statement.id);
|
||||
}
|
||||
|
||||
void codegen_block(FILE*, scope_t*, struct block*);
|
||||
|
||||
void codegen_if_statement(FILE* out, scope_t* scope, struct if_statement statement) {
|
||||
region_t* condition = codegen_expr(out, scope, statement.condition);
|
||||
if (!condition->is_temp) {
|
||||
condition = clone(condition);
|
||||
}
|
||||
|
||||
region_t *inverse_condition = NULL;
|
||||
if (statement.else_block) {
|
||||
inverse_condition = scope_add_tmp(scope, 1);
|
||||
move_to(inverse_condition); reset(); inc();
|
||||
}
|
||||
|
||||
move_to(condition);
|
||||
loop({
|
||||
if (inverse_condition) {
|
||||
move_to(inverse_condition); reset();
|
||||
}
|
||||
|
||||
codegen_block(out, scope, statement.if_block);
|
||||
|
||||
move_to(condition); reset();
|
||||
});
|
||||
|
||||
if (inverse_condition) {
|
||||
move_to(inverse_condition);
|
||||
loop({
|
||||
codegen_block(out, scope, statement.else_block);
|
||||
|
||||
move_to(inverse_condition); reset();
|
||||
});
|
||||
|
||||
scope_remove(scope, inverse_condition);
|
||||
}
|
||||
|
||||
scope_remove(scope, condition);
|
||||
}
|
||||
|
||||
void codegen_while_statement(FILE* out, scope_t* scope, struct while_statement statement) {
|
||||
region_t* condition = scope_add_tmp(scope, 1);
|
||||
move_to(condition); reset();
|
||||
|
||||
region_t* tmp = codegen_expr(out, scope, statement.condition);
|
||||
copy(tmp, condition);
|
||||
region_used(scope, tmp);
|
||||
|
||||
move_to(condition);
|
||||
loop({
|
||||
codegen_block(out, scope, statement.block);
|
||||
|
||||
tmp = codegen_expr(out, scope, statement.condition);
|
||||
move_to(condition); reset();
|
||||
copy(tmp, condition);
|
||||
|
||||
region_used(scope, tmp);
|
||||
|
||||
move_to(condition);
|
||||
});
|
||||
|
||||
scope_remove(scope, condition);
|
||||
}
|
||||
|
||||
|
||||
void codegen_expr_statement(FILE* out, scope_t* scope, struct expr_statement statement) {
|
||||
region_t* region = codegen_expr(out, scope, statement.expr);
|
||||
if (region->is_temp) {
|
||||
scope_remove(scope, region);
|
||||
}
|
||||
}
|
||||
|
||||
void codegen_statement(FILE* out, scope_t* scope, struct statement* statement) {
|
||||
switch(statement->kind) {
|
||||
case DECL_STATEMENT:
|
||||
codegen_decl_statement(out, scope, statement->assignment);
|
||||
break;
|
||||
case ASSIGNMENT_STATEMENT:
|
||||
codegen_assignment_statement(out, scope, statement->assignment);
|
||||
break;
|
||||
case EXPR_STATEMENT:
|
||||
codegen_expr_statement(out, scope, statement->expr);
|
||||
break;
|
||||
case IF_STATEMENT:
|
||||
codegen_if_statement(out, scope, statement->if_else);
|
||||
break;
|
||||
case WHILE_STATEMENT:
|
||||
codegen_while_statement(out, scope, statement->while_loop);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "statement kind: %d\n", statement->kind);
|
||||
panic("unknown statement kind");
|
||||
}
|
||||
// add newline after each statement
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
|
||||
void check_allocations(band_t* band) {
|
||||
size_t number_of_allocated_regions = band_number_of_regions(band);
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef CODEGEN_H
|
||||
#define CODEGEN_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "ast.h"
|
||||
#include "band.h"
|
||||
#include "scope.h"
|
||||
|
@ -26,6 +28,8 @@
|
|||
|
||||
#define reset_region(r) _reset_region(out, scope, r)
|
||||
|
||||
#define region_used(region) if (region->is_temp) { scope_remove(scope, region); }
|
||||
|
||||
void _move_to(FILE*, scope_t*, size_t);
|
||||
void _copy(FILE*, scope_t*, region_t*, region_t*);
|
||||
region_t* _clone(FILE*, scope_t*, region_t*);
|
||||
|
@ -34,4 +38,8 @@ void _reset_region(FILE*, scope_t*, region_t*);
|
|||
|
||||
int codegen(FILE*, struct block*);
|
||||
|
||||
region_t* codegen_expr(FILE*, scope_t*, struct expression*);
|
||||
void codegen_statement(FILE*, scope_t*, struct statement*);
|
||||
void codegen_block(FILE*, scope_t*, struct block*);
|
||||
|
||||
#endif
|
||||
|
|
690
compiler/src/codegen_expr.c
Normal file
690
compiler/src/codegen_expr.c
Normal file
|
@ -0,0 +1,690 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <alloca.h>
|
||||
|
||||
#include <error.h>
|
||||
|
||||
#include "codegen.h"
|
||||
#include "ast.h"
|
||||
#include "band.h"
|
||||
#include "scope.h"
|
||||
#include "builtins/builtins.h"
|
||||
|
||||
static void codegen_add_char(FILE* out, scope_t* scope, size_t position, char c) {
|
||||
move(position);
|
||||
reset();
|
||||
add(c);
|
||||
}
|
||||
|
||||
static region_t* codegen_literal_expr(FILE* out, scope_t* scope, struct literal_expression expr) {
|
||||
region_t* region = NULL;
|
||||
switch(expr.kind) {
|
||||
case NUMBER_LITERAL:
|
||||
if (expr.number > 255) {
|
||||
fprintf(stderr, "literal %lld greater than 255\n", expr.number);
|
||||
panic("number literal too big");
|
||||
}
|
||||
region = scope_add_tmp(scope, 1);
|
||||
move_to(region);
|
||||
reset(); add(expr.number);
|
||||
break;
|
||||
case CHAR_LITERAL:
|
||||
region = scope_add_tmp(scope, 1);
|
||||
codegen_add_char(out, scope, region->start, expr.ch);
|
||||
break;
|
||||
case STRING_LITERAL: {
|
||||
size_t l = strlen(expr.str); // don't copy \0
|
||||
region = scope_add_tmp(scope, strlen(expr.str));
|
||||
for (size_t i = 0; i < l; i++) {
|
||||
codegen_add_char(out, scope, region->start + i, expr.str[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
fprintf(stderr, "literal kind: %d\n", expr.kind);
|
||||
panic("unknown literal kind");
|
||||
}
|
||||
return region;
|
||||
}
|
||||
|
||||
static region_t* codegen_variable_expr(FILE* _, scope_t* scope, struct variable_expression expr) {
|
||||
region_t* region = scope_get(scope, expr.id);
|
||||
if (!region) {
|
||||
fprintf(stderr, "unknown variable: %s\n", expr.id);
|
||||
exit(1);
|
||||
}
|
||||
return region;
|
||||
}
|
||||
|
||||
static region_t* codegen_macro_expr(FILE* out, scope_t* scope, struct macro_expression expr) {
|
||||
builtin_t macro = find_builtin(expr.id);
|
||||
if (macro == NULL) {
|
||||
panic("unable to find builtin");
|
||||
}
|
||||
|
||||
region_t* arg = scope_get(scope, expr.argument);
|
||||
if (arg == NULL) {
|
||||
panic("argument has to be a variable");
|
||||
}
|
||||
|
||||
return macro(out, scope, 1, &arg);
|
||||
}
|
||||
|
||||
#define swap_or_clone(result, op1, op2, allow_swap) \
|
||||
if (op1->is_temp) { \
|
||||
result = op1; \
|
||||
} else if (op2->is_temp && allow_swap) { \
|
||||
result = op2; \
|
||||
op2 = op1; \
|
||||
} else { \
|
||||
result = clone(op1); \
|
||||
}
|
||||
|
||||
#define calc_prefix(allow_swap) \
|
||||
region_t* result; \
|
||||
swap_or_clone(result, op1, op2, allow_swap); \
|
||||
if (!op2->is_temp) { \
|
||||
op2 = clone(op2); \
|
||||
}
|
||||
|
||||
#define calc_postfix() \
|
||||
scope_remove(scope, op2); \
|
||||
return result;
|
||||
|
||||
static region_t* codegen_addition(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
calc_prefix(true);
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
dec();
|
||||
move_to(result); inc();
|
||||
move_to(op2);
|
||||
});
|
||||
|
||||
calc_postfix();
|
||||
}
|
||||
|
||||
static region_t* codegen_subtraction(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
calc_prefix(false);
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
dec();
|
||||
move_to(result); dec();
|
||||
move_to(op2);
|
||||
});
|
||||
|
||||
calc_postfix();
|
||||
}
|
||||
|
||||
static region_t* codegen_multiplication(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
calc_prefix(true);
|
||||
|
||||
// make sure to not change op1
|
||||
op1 = clone(op1);
|
||||
region_t* tmp = clone(op1);
|
||||
|
||||
move_to(result); reset();
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
dec();
|
||||
|
||||
move_to(tmp);
|
||||
loop({
|
||||
dec();
|
||||
move_to(result); inc();
|
||||
move_to(tmp);
|
||||
});
|
||||
copy(op1, tmp);
|
||||
|
||||
move_to(op2);
|
||||
});
|
||||
|
||||
scope_remove(scope, tmp);
|
||||
scope_remove(scope, op1);
|
||||
|
||||
calc_postfix();
|
||||
}
|
||||
|
||||
static region_t* codegen_division(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
region_t* result = scope_add_tmp(scope, 1);
|
||||
move_to(result); reset();
|
||||
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
move_to(tmp); reset();
|
||||
copy(op2, tmp);
|
||||
|
||||
region_t* tmp2 = scope_add_tmp(scope, 1);
|
||||
|
||||
region_t* flag = scope_add_tmp(scope, 1);
|
||||
|
||||
move_to(op1);
|
||||
loop({
|
||||
dec();
|
||||
|
||||
move_to(tmp); dec();
|
||||
|
||||
move_to(flag); reset(); inc();
|
||||
|
||||
move_to(tmp2); reset();
|
||||
copy(tmp, tmp2);
|
||||
move_to(tmp2);
|
||||
loop({
|
||||
move_to(flag); reset();
|
||||
move_to(tmp2); reset();
|
||||
});
|
||||
|
||||
move_to(flag);
|
||||
loop({
|
||||
move_to(result); inc();
|
||||
copy(op2, tmp);
|
||||
move_to(flag); reset();
|
||||
});
|
||||
|
||||
move_to(op1);
|
||||
});
|
||||
|
||||
scope_remove(scope, flag);
|
||||
scope_remove(scope, tmp2);
|
||||
scope_remove(scope, tmp);
|
||||
scope_remove(scope, op1);
|
||||
scope_remove(scope, op2);
|
||||
return result;
|
||||
}
|
||||
|
||||
static region_t* codegen_modulo(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
move_to(tmp); reset();
|
||||
copy(op2, tmp);
|
||||
|
||||
region_t* tmp2 = scope_add_tmp(scope, 1);
|
||||
|
||||
region_t* flag = scope_add_tmp(scope, 1);
|
||||
|
||||
move_to(op1);
|
||||
loop({
|
||||
dec();
|
||||
|
||||
move_to(tmp); dec();
|
||||
|
||||
move_to(flag); reset(); inc();
|
||||
|
||||
move_to(tmp2); reset();
|
||||
copy(tmp, tmp2);
|
||||
move_to(tmp2);
|
||||
loop({
|
||||
move_to(flag); reset();
|
||||
move_to(tmp2); reset();
|
||||
});
|
||||
|
||||
move_to(flag);
|
||||
loop({
|
||||
copy(op2, tmp);
|
||||
move_to(flag); reset();
|
||||
});
|
||||
|
||||
move_to(op1);
|
||||
});
|
||||
|
||||
move_to(tmp);
|
||||
loop({
|
||||
loop({
|
||||
dec();
|
||||
move_to(op2); dec();
|
||||
move_to(tmp);
|
||||
});
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
dec();
|
||||
move_to(op1); inc();
|
||||
move_to(op2);
|
||||
});
|
||||
|
||||
move_to(tmp);
|
||||
});
|
||||
|
||||
scope_remove(scope, flag);
|
||||
scope_remove(scope, tmp2);
|
||||
scope_remove(scope, tmp);
|
||||
scope_remove(scope, op2);
|
||||
return op1;
|
||||
}
|
||||
|
||||
static region_t* codegen_equals(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
dec();
|
||||
move_to(op1); dec();
|
||||
move_to(op2);
|
||||
});
|
||||
inc();
|
||||
|
||||
move_to(op1);
|
||||
loop({
|
||||
move_to(op2); dec();
|
||||
move_to(op1); reset();
|
||||
});
|
||||
|
||||
scope_remove(scope, op1);
|
||||
return op2;
|
||||
}
|
||||
|
||||
static region_t* codegen_not_equals(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
dec();
|
||||
move_to(op1); dec();
|
||||
move_to(op2);
|
||||
});
|
||||
|
||||
scope_remove(scope, op2);
|
||||
return op1;
|
||||
}
|
||||
|
||||
static region_t* codegen_conjunction(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
move_to(tmp); reset();
|
||||
|
||||
move_to(op1);
|
||||
loop({
|
||||
move_to(op2);
|
||||
loop({
|
||||
move_to(tmp); inc();
|
||||
move_to(op2); reset();
|
||||
});
|
||||
|
||||
move_to(op1); reset();
|
||||
});
|
||||
|
||||
scope_remove(scope, op1);
|
||||
scope_remove(scope, op2);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static region_t* codegen_disjunction(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
move_to(tmp); reset();
|
||||
|
||||
move_to(op1);
|
||||
loop({
|
||||
move_to(tmp); inc();
|
||||
move_to(op1); reset();
|
||||
});
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
move_to(tmp); inc();
|
||||
move_to(op2); reset();
|
||||
});
|
||||
|
||||
move_to(tmp);
|
||||
loop({
|
||||
move_to(op1); inc();
|
||||
move_to(tmp); reset();
|
||||
})
|
||||
|
||||
scope_remove(scope, op2);
|
||||
scope_remove(scope, tmp);
|
||||
|
||||
return op1;
|
||||
}
|
||||
|
||||
static region_t* codegen_greater_than(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
region_t* result = scope_add_tmp(scope, 1);
|
||||
move_to(result); reset();
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
region_t* op2_is_empty = scope_add_tmp(scope, 1);
|
||||
|
||||
move_to(op1);
|
||||
loop({
|
||||
dec();
|
||||
move_to(op2_is_empty); reset(); inc();
|
||||
move_to(tmp); reset();
|
||||
move_to(op2);
|
||||
loop({
|
||||
move_to(tmp); inc();
|
||||
move_to(op2_is_empty); reset();
|
||||
move_to(op2); dec();
|
||||
});
|
||||
|
||||
move_to(tmp);
|
||||
loop({
|
||||
move_to(op2); inc();
|
||||
move_to(tmp); dec();
|
||||
});
|
||||
|
||||
move_to(op2); dec();
|
||||
|
||||
move_to(op2_is_empty);
|
||||
loop({
|
||||
move_to(result); inc();
|
||||
move_to(op1); reset();
|
||||
move_to(op2_is_empty); dec();
|
||||
});
|
||||
|
||||
move_to(op1);
|
||||
});
|
||||
|
||||
scope_remove(scope, op1);
|
||||
scope_remove(scope, op2);
|
||||
scope_remove(scope, tmp);
|
||||
scope_remove(scope, op2_is_empty);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static region_t* codegen_less_than(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
region_t* result = scope_add_tmp(scope, 1);
|
||||
move_to(result); reset();
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
region_t* op1_is_empty = scope_add_tmp(scope, 1);
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
dec();
|
||||
move_to(op1_is_empty); reset(); inc();
|
||||
move_to(tmp); reset();
|
||||
move_to(op1);
|
||||
loop({
|
||||
move_to(tmp); inc();
|
||||
move_to(op1_is_empty); reset();
|
||||
move_to(op1); dec();
|
||||
});
|
||||
|
||||
move_to(tmp);
|
||||
loop({
|
||||
move_to(op1); inc();
|
||||
move_to(tmp); dec();
|
||||
});
|
||||
|
||||
move_to(op1); dec();
|
||||
|
||||
move_to(op1_is_empty);
|
||||
loop({
|
||||
move_to(result); inc();
|
||||
move_to(op2); reset();
|
||||
move_to(op1_is_empty); dec();
|
||||
});
|
||||
|
||||
move_to(op2);
|
||||
});
|
||||
|
||||
scope_remove(scope, op1);
|
||||
scope_remove(scope, op2);
|
||||
scope_remove(scope, tmp);
|
||||
scope_remove(scope, op1_is_empty);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static region_t* codegen_greater_than_or_equals(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
region_t* result = scope_add_tmp(scope, 1);
|
||||
move_to(result); reset(); inc();
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
region_t* op1_is_empty = scope_add_tmp(scope, 1);
|
||||
|
||||
move_to(op2);
|
||||
loop({
|
||||
dec();
|
||||
move_to(op1_is_empty); reset(); inc();
|
||||
move_to(tmp); reset();
|
||||
move_to(op1);
|
||||
loop({
|
||||
move_to(tmp); inc();
|
||||
move_to(op1_is_empty); reset();
|
||||
move_to(op1); dec();
|
||||
});
|
||||
|
||||
move_to(tmp);
|
||||
loop({
|
||||
move_to(op1); inc();
|
||||
move_to(tmp); dec();
|
||||
});
|
||||
|
||||
move_to(op1); dec();
|
||||
|
||||
move_to(op1_is_empty);
|
||||
loop({
|
||||
move_to(result); reset();
|
||||
move_to(op2); reset();
|
||||
move_to(op1_is_empty); dec();
|
||||
});
|
||||
|
||||
move_to(op2);
|
||||
});
|
||||
|
||||
scope_remove(scope, op1);
|
||||
scope_remove(scope, op2);
|
||||
scope_remove(scope, tmp);
|
||||
scope_remove(scope, op1_is_empty);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static region_t* codegen_less_than_or_equals(FILE* out, scope_t* scope, region_t* op1, region_t* op2) {
|
||||
if (!op1->is_temp) {
|
||||
op1 = clone(op1);
|
||||
}
|
||||
if (!op2->is_temp) {
|
||||
op2 = clone(op2);
|
||||
}
|
||||
|
||||
region_t* result = scope_add_tmp(scope, 1);
|
||||
move_to(result); reset(); inc();
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
region_t* op2_is_empty = scope_add_tmp(scope, 1);
|
||||
|
||||
move_to(op1);
|
||||
loop({
|
||||
dec();
|
||||
move_to(op2_is_empty); reset(); inc();
|
||||
move_to(tmp); reset();
|
||||
move_to(op2);
|
||||
loop({
|
||||
move_to(tmp); inc();
|
||||
move_to(op2_is_empty); reset();
|
||||
move_to(op2); dec();
|
||||
});
|
||||
|
||||
move_to(tmp);
|
||||
loop({
|
||||
move_to(op2); inc();
|
||||
move_to(tmp); dec();
|
||||
});
|
||||
|
||||
move_to(op2); dec();
|
||||
|
||||
move_to(op2_is_empty);
|
||||
loop({
|
||||
move_to(result); reset();
|
||||
move_to(op1); reset();
|
||||
move_to(op2_is_empty); dec();
|
||||
});
|
||||
|
||||
move_to(op1);
|
||||
});
|
||||
|
||||
scope_remove(scope, op1);
|
||||
scope_remove(scope, op2);
|
||||
scope_remove(scope, tmp);
|
||||
scope_remove(scope, op2_is_empty);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static region_t* codegen_negation(FILE* out, scope_t* scope, region_t* op) {
|
||||
if (!op->is_temp) {
|
||||
op = clone(op);
|
||||
}
|
||||
|
||||
region_t* result = scope_add_tmp(scope, 1);
|
||||
move_to(result); reset(); inc();
|
||||
|
||||
move_to(op);
|
||||
loop({
|
||||
move_to(result); dec();
|
||||
move_to(op); reset();
|
||||
});
|
||||
|
||||
scope_remove(scope, op);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static region_t* codegen_calc_expr(FILE* out, scope_t* scope, struct calc_expression expr) {
|
||||
region_t* operand1 = codegen_expr(out, scope, expr.operand1);
|
||||
region_t* operand2 = NULL;
|
||||
if (expr.operand2) {
|
||||
operand2 = codegen_expr(out, scope, expr.operand2);
|
||||
}
|
||||
|
||||
region_t* result = NULL;
|
||||
|
||||
switch (expr.operator) {
|
||||
case ADDITION:
|
||||
result = codegen_addition(out, scope, operand1, operand2);
|
||||
break;
|
||||
case SUBTRACTION:
|
||||
result = codegen_subtraction(out, scope, operand1, operand2);
|
||||
break;
|
||||
case MULTIPLICATION:
|
||||
result = codegen_multiplication(out, scope, operand1, operand2);
|
||||
break;
|
||||
case DIVISION:
|
||||
result = codegen_division(out, scope, operand1, operand2);
|
||||
break;
|
||||
case MODULO:
|
||||
result = codegen_modulo(out, scope, operand1, operand2);
|
||||
break;
|
||||
case EQUALS:
|
||||
result = codegen_equals(out, scope, operand1, operand2);
|
||||
break;
|
||||
case GREATER_THAN:
|
||||
result = codegen_greater_than(out, scope, operand1, operand2);
|
||||
break;
|
||||
case LESS_THAN:
|
||||
result = codegen_less_than(out, scope, operand1, operand2);
|
||||
break;
|
||||
case GREATER_EQUALS:
|
||||
result = codegen_greater_than_or_equals(out, scope, operand1, operand2);
|
||||
break;
|
||||
case LESS_EQUALS:
|
||||
result = codegen_less_than_or_equals(out, scope, operand1, operand2);
|
||||
break;
|
||||
case NOT_EQUALS:
|
||||
result = codegen_not_equals(out, scope, operand1, operand2);
|
||||
break;
|
||||
case CONJUNCTION:
|
||||
result = codegen_conjunction(out, scope, operand1, operand2);
|
||||
break;
|
||||
case DISJUNCTION:
|
||||
result = codegen_disjunction(out, scope, operand1, operand2);
|
||||
break;
|
||||
case NEGATION:
|
||||
result = codegen_negation(out, scope, operand1);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown operator: %d\n", expr.operator);
|
||||
panic("unknown operator");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static region_t* codegen_builtin_expr(FILE* out, scope_t* scope, struct builtin_call_expression expr) {
|
||||
builtin_t builtin = find_builtin(expr.id);
|
||||
|
||||
region_t** args = alloca(expr.argument_number * sizeof(region_t*));
|
||||
|
||||
for (size_t i = 0; i < expr.argument_number; i++) {
|
||||
args[i] = codegen_expr(out, scope, expr.arguments[i]);
|
||||
}
|
||||
|
||||
return builtin(out, scope, expr.argument_number, args);
|
||||
}
|
||||
|
||||
region_t* codegen_expr(FILE* out, scope_t* scope, struct expression* expr) {
|
||||
switch(expr->kind) {
|
||||
case LITERAL:
|
||||
return codegen_literal_expr(out, scope, expr->literal);
|
||||
case VARIABLE:
|
||||
return codegen_variable_expr(out, scope, expr->variable);
|
||||
case MACRO:
|
||||
return codegen_macro_expr(out, scope, expr->macro);
|
||||
case BUILTIN_CALL:
|
||||
return codegen_builtin_expr(out, scope, expr->builtin_call);
|
||||
case CALCULATION:
|
||||
return codegen_calc_expr(out, scope, expr->calc);
|
||||
default:
|
||||
fprintf(stderr, "expression kind: %d\n", expr->kind);
|
||||
panic("unknown expression kind");
|
||||
return NULL;
|
||||
}
|
||||
}
|
170
compiler/src/codegen_stat.c
Normal file
170
compiler/src/codegen_stat.c
Normal file
|
@ -0,0 +1,170 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <alloca.h>
|
||||
|
||||
#include <error.h>
|
||||
|
||||
#include "codegen.h"
|
||||
#include "ast.h"
|
||||
#include "band.h"
|
||||
#include "scope.h"
|
||||
|
||||
|
||||
static void codegen_assignment_statement(FILE* out, scope_t* scope, struct assignment_statement statement) {
|
||||
region_t* region = codegen_expr(out, scope, statement.value);
|
||||
region_t* var = scope_get(scope, statement.id);
|
||||
if (!var) {
|
||||
fprintf(stderr, "variable not found: %s\n", statement.id);
|
||||
panic("unknown variable");
|
||||
}
|
||||
|
||||
if (var->start != region->start) {
|
||||
reset_region(var);
|
||||
copy(region, var);
|
||||
}
|
||||
|
||||
region_used(region);
|
||||
}
|
||||
|
||||
// only used by decl_statement
|
||||
// -> TODO refactor to clone macro
|
||||
static region_t* clone_region(FILE* out, scope_t* scope, region_t* original) {
|
||||
region_t* tmp = scope_add_tmp(scope, 1);
|
||||
region_t* clone = scope_add_tmp(scope, original->size);
|
||||
|
||||
move_to(tmp); reset();
|
||||
for (size_t i = 0; i < original->size; i++) {
|
||||
move_offset(clone, i); reset();
|
||||
|
||||
move_offset(original, i);
|
||||
loop({
|
||||
dec();
|
||||
move_to(tmp);
|
||||
inc();
|
||||
move_offset(clone, i);
|
||||
inc();
|
||||
move_offset(original, i);
|
||||
});
|
||||
|
||||
move_to(tmp);
|
||||
loop({
|
||||
dec();
|
||||
move_offset(original, i);
|
||||
inc();
|
||||
move_to(tmp);
|
||||
});
|
||||
}
|
||||
|
||||
scope_remove(scope, tmp);
|
||||
return clone;
|
||||
}
|
||||
|
||||
static void codegen_decl_statement(FILE* out, scope_t* scope, struct assignment_statement statement) {
|
||||
if (scope_get(scope, statement.id)) {
|
||||
fprintf(stderr, "variable exists: %s\n", statement.id);
|
||||
panic("variable exists");
|
||||
}
|
||||
|
||||
region_t* region = codegen_expr(out, scope, statement.value);
|
||||
|
||||
if (!region->is_temp) {
|
||||
region = clone_region(out, scope, region);
|
||||
}
|
||||
|
||||
scope_existing(scope, region, statement.id);
|
||||
}
|
||||
|
||||
static void codegen_if_statement(FILE* out, scope_t* scope, struct if_statement statement) {
|
||||
region_t* condition = codegen_expr(out, scope, statement.condition);
|
||||
if (!condition->is_temp) {
|
||||
condition = clone(condition);
|
||||
}
|
||||
|
||||
region_t *inverse_condition = NULL;
|
||||
if (statement.else_block) {
|
||||
inverse_condition = scope_add_tmp(scope, 1);
|
||||
move_to(inverse_condition); reset(); inc();
|
||||
}
|
||||
|
||||
move_to(condition);
|
||||
loop({
|
||||
if (inverse_condition) {
|
||||
move_to(inverse_condition); reset();
|
||||
}
|
||||
|
||||
codegen_block(out, scope, statement.if_block);
|
||||
|
||||
move_to(condition); reset();
|
||||
});
|
||||
|
||||
if (inverse_condition) {
|
||||
move_to(inverse_condition);
|
||||
loop({
|
||||
codegen_block(out, scope, statement.else_block);
|
||||
|
||||
move_to(inverse_condition); reset();
|
||||
});
|
||||
|
||||
scope_remove(scope, inverse_condition);
|
||||
}
|
||||
|
||||
scope_remove(scope, condition);
|
||||
}
|
||||
|
||||
static void codegen_while_statement(FILE* out, scope_t* scope, struct while_statement statement) {
|
||||
region_t* condition = scope_add_tmp(scope, 1);
|
||||
move_to(condition); reset();
|
||||
|
||||
region_t* tmp = codegen_expr(out, scope, statement.condition);
|
||||
copy(tmp, condition);
|
||||
region_used(tmp);
|
||||
|
||||
move_to(condition);
|
||||
loop({
|
||||
codegen_block(out, scope, statement.block);
|
||||
|
||||
tmp = codegen_expr(out, scope, statement.condition);
|
||||
move_to(condition); reset();
|
||||
copy(tmp, condition);
|
||||
|
||||
region_used(tmp);
|
||||
|
||||
move_to(condition);
|
||||
});
|
||||
|
||||
scope_remove(scope, condition);
|
||||
}
|
||||
|
||||
|
||||
static void codegen_expr_statement(FILE* out, scope_t* scope, struct expr_statement statement) {
|
||||
region_t* region = codegen_expr(out, scope, statement.expr);
|
||||
if (region->is_temp) {
|
||||
scope_remove(scope, region);
|
||||
}
|
||||
}
|
||||
|
||||
void codegen_statement(FILE* out, scope_t* scope, struct statement* statement) {
|
||||
switch(statement->kind) {
|
||||
case DECL_STATEMENT:
|
||||
codegen_decl_statement(out, scope, statement->assignment);
|
||||
break;
|
||||
case ASSIGNMENT_STATEMENT:
|
||||
codegen_assignment_statement(out, scope, statement->assignment);
|
||||
break;
|
||||
case EXPR_STATEMENT:
|
||||
codegen_expr_statement(out, scope, statement->expr);
|
||||
break;
|
||||
case IF_STATEMENT:
|
||||
codegen_if_statement(out, scope, statement->if_else);
|
||||
break;
|
||||
case WHILE_STATEMENT:
|
||||
codegen_while_statement(out, scope, statement->while_loop);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "statement kind: %d\n", statement->kind);
|
||||
panic("unknown statement kind");
|
||||
}
|
||||
// add newline after each statement
|
||||
fprintf(out, "\n");
|
||||
}
|
Loading…
Reference in a new issue