Files
c3c/src/compiler/llvm_codegen_stmt.c

717 lines
22 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the GNU LGPLv3.0 license
// a copy of which can be found in the LICENSE file.
#include "llvm_codegen_internal.h"
static void gencontext_pop_break_continue(GenContext *context);
static void gencontext_push_break_continue(GenContext *context, LLVMBasicBlockRef break_block, LLVMBasicBlockRef continue_block,
LLVMBasicBlockRef next_block);
void gencontext_emit_compound_stmt(GenContext *context, Ast *ast)
{
assert(ast->ast_kind == AST_COMPOUND_STMT);
VECEACH(ast->compound_stmt.stmts, i)
{
gencontext_emit_stmt(context, ast->compound_stmt.stmts[i]);
}
gencontext_emit_defer(context, ast->compound_stmt.defer_list.start, ast->compound_stmt.defer_list.end);
}
static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast)
{
Decl *decl = ast->declare_stmt;
decl->ref = gencontext_emit_alloca(context, llvm_type(type_reduced(decl->type)), decl->name);
// TODO NRVO
// TODO debug info
/*
if (EmitDebugInfo && HaveInsertPoint()) {
Address DebugAddr = address;
bool UsePointerValue = NRVO && ReturnValuePointer.isValid();
DI->setLocation(D.getLocation());
// If NRVO, use a pointer to the return address.
if (UsePointerValue)
DebugAddr = ReturnValuePointer;
(void)DI->EmitDeclareOfAutoVariable(&D, DebugAddr.getPointer(), Builder,
UsePointerValue);
}
*/
if (decl->var.init_expr)
{
gencontext_emit_assign_expr(context, decl->ref, decl->var.init_expr);
return decl->ref;
}
return decl->ref;
}
void gencontext_emit_decl_expr_list_ignore_result(GenContext *context, Ast *ast)
{
assert(ast->ast_kind == AST_DECL_EXPR_LIST);
VECEACH(ast->decl_expr_stmt, i)
{
gencontext_emit_stmt(context, ast->decl_expr_stmt[i]);
}
}
LLVMValueRef gencontext_emit_decl_expr_list(GenContext *context, Ast *ast, bool bool_cast)
{
assert(ast->ast_kind == AST_DECL_EXPR_LIST);
size_t size = vec_size(ast->decl_expr_stmt);
size_t last_index = size - 1;
for (size_t i = 0; i < last_index; i++)
{
gencontext_emit_stmt(context, ast->decl_expr_stmt[i]);
}
Ast *last = ast->decl_expr_stmt[last_index];
LLVMValueRef result;
Type *type;
switch (last->ast_kind)
{
case AST_EXPR_STMT:
type = last->expr_stmt->type;
result = gencontext_emit_expr(context, last->expr_stmt);
break;
case AST_DECLARE_STMT:
type = last->declare_stmt->var.type_info->type;
result = gencontext_emit_load(context, type, gencontext_emit_decl(context, last));
break;
default:
UNREACHABLE
}
if (bool_cast)
{
type = type->canonical;
if (type->type_kind != TYPE_BOOL)
{
CastKind cast = cast_to_bool_kind(type);
result = gencontext_emit_cast(context, cast, result, type, type_bool);
}
}
return result;
}
void gencontext_emit_jmp(GenContext *context, LLVMBasicBlockRef block)
{
gencontext_emit_br(context, block);
LLVMBasicBlockRef post_jump_block = gencontext_create_free_block(context, "jmp");
gencontext_emit_block(context, post_jump_block);
}
static inline void gencontext_emit_return(GenContext *context, Ast *ast)
{
// Ensure we are on a branch that is non empty.
if (!gencontext_check_block_branch_emit(context)) return;
LLVMValueRef ret_value = ast->return_stmt.expr ? gencontext_emit_expr(context, ast->return_stmt.expr) : NULL;
gencontext_emit_defer(context, ast->return_stmt.defer, NULL);
// Are we in an expression block?
if (context->expr_block_exit)
{
if (context->return_out)
{
LLVMBuildStore(context->builder, ret_value, context->return_out);
}
gencontext_emit_jmp(context, context->expr_block_exit);
return;
}
if (!ret_value)
{
gencontext_emit_implicit_return(context);
}
else
{
if (context->return_out)
{
LLVMBuildStore(context->builder, ret_value, context->return_out);
gencontext_emit_implicit_return(context);
}
else
{
LLVMBuildRet(context->builder, ret_value);
}
}
context->current_block = NULL;
LLVMBasicBlockRef post_ret_block = gencontext_create_free_block(context, "ret");
gencontext_emit_block(context, post_ret_block);
}
static inline void gencontext_emit_throw(GenContext *context, Ast *ast)
{
// Ensure we are on a branch that is non empty.
if (!gencontext_check_block_branch_emit(context)) return;
LLVMValueRef error_val = gencontext_emit_expr(context, ast->throw_stmt.throw_value);
gencontext_emit_defer(context, ast->throw_stmt.defer, NULL);
// In the case that the throw actually contains a single error, but the function is throwing an error union,
// we must insert a conversion.
if (context->cur_func_decl->func.function_signature.error_return != ERROR_RETURN_ONE &&
ast->throw_stmt.throw_value->type->type_kind == TYPE_ERROR)
{
error_val = gencontext_emit_cast(context, CAST_ERREU, error_val, type_error_union, ast->throw_stmt.throw_value->type);
}
gencontext_emit_return_value(context, error_val);
LLVMBasicBlockRef post_throw_block = gencontext_create_free_block(context, "throw");
gencontext_emit_block(context, post_throw_block);
}
void gencontext_emit_if(GenContext *context, Ast *ast)
{
if (ast->if_stmt.decl) gencontext_emit_decl_expr_list_ignore_result(context, ast->if_stmt.decl);
// We need at least the exit block and the "then" block.
LLVMBasicBlockRef exit_block = LLVMCreateBasicBlockInContext(context->context, "if.exit");
LLVMBasicBlockRef then_block = LLVMCreateBasicBlockInContext(context->context, "if.then");
LLVMBasicBlockRef else_block = NULL;
// We have an optional else block.
if (ast->if_stmt.else_body)
{
else_block = LLVMCreateBasicBlockInContext(context->context, "if.else");
}
// Output boolean value and switch.
LLVMValueRef value = gencontext_emit_decl_expr_list(context, ast->if_stmt.cond, true);
gencontext_emit_cond_br(context, value, then_block, else_block ? else_block : exit_block);
// Emit the 'then' code.
gencontext_emit_block(context, then_block);
gencontext_emit_stmt(context, ast->if_stmt.then_body);
// Jump to exit.
gencontext_emit_br(context, exit_block);
// Emit the 'else' branch if present.
if (else_block)
{
gencontext_emit_block(context, else_block);
gencontext_emit_stmt(context, ast->if_stmt.else_body);
gencontext_emit_br(context, exit_block);
}
// And now we just emit the exit block.
gencontext_emit_block(context, exit_block);
}
static void gencontext_push_break_continue(GenContext *context, LLVMBasicBlockRef break_block,
LLVMBasicBlockRef continue_block, LLVMBasicBlockRef next_block)
{
size_t index = context->break_continue_stack_index++;
if (index == BREAK_STACK_MAX - 1)
{
error_exit("Exhausted break/continue stack - exceeded %d entries.", BREAK_STACK_MAX);
}
if (!index)
{
context->break_continue_stack[index].continue_block = continue_block;
context->break_continue_stack[index].break_block = break_block;
context->break_continue_stack[index].next_block = next_block;
return;
}
context->break_continue_stack[index].continue_block = continue_block ? continue_block : context->break_continue_stack[index - 1].continue_block;
context->break_continue_stack[index].next_block = next_block ? next_block : context->break_continue_stack[index - 1].next_block;
context->break_continue_stack[index].break_block = break_block ? break_block : context->break_continue_stack[index - 1].break_block;
}
static void gencontext_pop_break_continue(GenContext *context)
{
assert(context->break_continue_stack_index);
context->break_continue_stack_index--;
}
void gencontext_emit_for_stmt(GenContext *context, Ast *ast)
{
// First, emit all inits.
if (ast->for_stmt.init) gencontext_emit_decl_expr_list_ignore_result(context, ast->for_stmt.init);
// We have 3 optional parts, which makes this code bit complicated.
LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "for.exit");
LLVMBasicBlockRef inc_block = ast->for_stmt.incr ? gencontext_create_free_block(context, "for.inc") : NULL;
LLVMBasicBlockRef body_block = ast->for_stmt.body->compound_stmt.stmts ? gencontext_create_free_block(context, "for.body") : NULL;
LLVMBasicBlockRef cond_block = ast->for_stmt.cond ? gencontext_create_free_block(context, "for.cond") : NULL;
// A loop must either have a body or an inc.
// This type of for loop is forbidden:
// for (;;);
assert((cond_block || inc_block || body_block) && "For has no body, no inc and no cond.");
// Break is simple it always jumps out.
// For continue:
// 1. If there is inc, jump to the condition
// 2. If there is no condition, jump to the body.
gencontext_push_break_continue(context,
exit_block,
inc_block ? inc_block : (cond_block ? cond_block : body_block),
NULL);
LLVMValueRef value = NULL;
if (cond_block)
{
// Emit cond
gencontext_emit_br(context, cond_block);
gencontext_emit_block(context, cond_block);
value = gencontext_emit_expr(context, ast->for_stmt.cond);
// If we have a body, conditionally jump to it.
if (body_block)
{
gencontext_emit_cond_br(context, value, body_block, exit_block);
}
else
{
// Otherwise jump to inc or cond depending on what's available.
gencontext_emit_cond_br(context, value, inc_block ? inc_block : cond_block, exit_block);
}
}
if (body_block)
{
if (!cond_block)
{
// We don't have a cond, so we need to unconditionally jump here.
gencontext_emit_br(context, body_block);
}
gencontext_emit_block(context, body_block);
gencontext_emit_stmt(context, ast->for_stmt.body);
// IMPROVE handle continue/break.
if (inc_block)
{
gencontext_emit_br(context, inc_block);
}
}
if (inc_block)
{
// Emit the block
gencontext_emit_block(context, inc_block);
gencontext_emit_expr(context, ast->for_stmt.incr);
}
// Loop back.
gencontext_emit_br(context, cond_block ? cond_block : (body_block ? body_block : inc_block));
// And insert exit block
gencontext_emit_block(context, exit_block);
gencontext_pop_break_continue(context);
}
void gencontext_emit_while_stmt(GenContext *context, Ast *ast)
{
// First, emit all inits.
if (ast->while_stmt.decl) gencontext_emit_decl_expr_list_ignore_result(context, ast->while_stmt.decl);
LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "while.exit");
LLVMBasicBlockRef begin_block = gencontext_create_free_block(context, "while.begin");
LLVMBasicBlockRef body_block = ast->while_stmt.body->compound_stmt.stmts ? gencontext_create_free_block(context, "while.body") : NULL;
gencontext_push_break_continue(context, exit_block, begin_block, NULL);
// Emit cond
gencontext_emit_br(context, begin_block);
gencontext_emit_block(context, begin_block);
DeferList defers = { NULL, NULL };
Ast *cond = ast->while_stmt.cond;
if (cond->ast_kind == AST_SCOPED_STMT)
{
defers = cond->scoped_stmt.defers;
cond = cond->scoped_stmt.stmt;
}
LLVMValueRef value = gencontext_emit_decl_expr_list(context, cond, true);
// If we have a body, conditionally jump to it.
if (body_block)
{
gencontext_emit_cond_br(context, value, body_block, exit_block);
}
else
{
// Emit defers
gencontext_emit_defer(context, defers.start, defers.end);
// Otherwise jump to inc or cond depending on what's available.
gencontext_emit_cond_br(context, value, begin_block, exit_block);
}
if (body_block)
{
gencontext_emit_block(context, body_block);
gencontext_emit_stmt(context, ast->while_stmt.body);
// Emit defers
gencontext_emit_defer(context, defers.start, defers.end);
}
// Loop back.
gencontext_emit_br(context, begin_block);
// And insert exit block
gencontext_emit_block(context, exit_block);
// Emit defers
gencontext_emit_defer(context, defers.start, defers.end);
gencontext_pop_break_continue(context);
}
void gencontext_emit_do_stmt(GenContext *context, Ast *ast)
{
LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "do.exit");
LLVMBasicBlockRef cond_block = ast->do_stmt.expr ? gencontext_create_free_block(context, "do.cond") : NULL;
LLVMBasicBlockRef body_block = ast->do_stmt.body ? gencontext_create_free_block(context, "do.body") : NULL;
// A loop must either have a body or an inc.
// This type do-while for loop is forbidden:
// do { } while (1);
assert((cond_block || body_block) && "Do has no body and no cond.");
// Break is simple it always jumps out.
// For continue: if there is no condition, jump to the body.
gencontext_push_break_continue(context, exit_block, cond_block ? cond_block : body_block, NULL);
if (body_block)
{
// Emit the body
gencontext_emit_br(context, body_block);
gencontext_emit_block(context, body_block);
gencontext_emit_stmt(context, ast->do_stmt.body);
}
if (cond_block)
{
gencontext_emit_br(context, cond_block);
gencontext_emit_block(context, cond_block);
LLVMValueRef value = gencontext_emit_expr(context, ast->do_stmt.expr);
gencontext_emit_cond_br(context, value, body_block ? body_block : cond_block, exit_block);
}
else
{
// Branch to the beginning of the block
gencontext_emit_br(context, body_block);
}
// Emit the exit block.
gencontext_emit_block(context, exit_block);
gencontext_pop_break_continue(context);
}
void gencontext_emit_label(GenContext *context, Ast *ast)
{
gencontext_emit_br(context, ast->label_stmt.backend_value);
gencontext_emit_block(context, ast->label_stmt.backend_value);
context->current_block_is_target = true;
}
void gencontext_emit_switch(GenContext *context, Ast *ast)
{
// TODO check defer correctness
if (ast->switch_stmt.decl) gencontext_emit_decl_expr_list_ignore_result(context, ast->switch_stmt.decl);
LLVMValueRef switch_value = gencontext_emit_decl_expr_list(context, ast->switch_stmt.cond, false);
size_t cases = vec_size(ast->switch_stmt.cases);
if (!cases)
{
// No body or default is empty, just exit after the value.
return;
}
Ast *default_case = NULL;
for (unsigned i = 0; i < cases; i++)
{
Ast *case_stmt = ast->switch_stmt.cases[i];
if (!case_stmt->case_stmt.expr)
{
if (case_stmt->case_stmt.body)
{
case_stmt->case_stmt.backend_value = gencontext_create_free_block(context, "switch.default");
}
default_case = case_stmt;
}
else if (case_stmt->case_stmt.body)
{
case_stmt->case_stmt.backend_value = gencontext_create_free_block(context, "switch.case");
}
}
LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "switch.exit");
// We will now treat the fallthrough cases:
// switch (i)
// {
// case 1:
// case 2:
// do_something();
// default:
// }
LLVMBasicBlockRef next_block = exit_block;
for (unsigned i = cases; i > 0; i--)
{
Ast *case_stmt = ast->switch_stmt.cases[i - 1];
if (case_stmt->case_stmt.backend_value)
{
next_block = case_stmt->case_stmt.backend_value;
continue;
}
case_stmt->case_stmt.backend_value = next_block;
}
gencontext_push_break_continue(context, exit_block, NULL, NULL);
LLVMValueRef switch_stmt = LLVMBuildSwitch(context->builder, switch_value, default_case ? default_case->case_stmt.backend_value : exit_block, cases);
context->current_block = NULL;
for (unsigned i = 0; i < cases; i++)
{
Ast *case_stmt = ast->switch_stmt.cases[i];
LLVMBasicBlockRef block = case_stmt->case_stmt.backend_value;
if (case_stmt != default_case)
{
LLVMValueRef case_value = gencontext_emit_expr(context, case_stmt->case_stmt.expr);
LLVMAddCase(switch_stmt, case_value, block);
}
// Skip fallthroughs.
if (!case_stmt->case_stmt.body) continue;
gencontext_emit_block(context, block);
// IMPORTANT!
context->current_block_is_target = true;
gencontext_push_break_continue(context, NULL, NULL, i < cases - 1 ? ast->switch_stmt.cases[i + 1]->case_stmt.backend_value : exit_block);
gencontext_emit_stmt(context, case_stmt->case_stmt.body);
gencontext_pop_break_continue(context);
gencontext_emit_br(context, exit_block);
}
gencontext_pop_break_continue(context);
gencontext_emit_block(context, exit_block);
}
LLVMValueRef gencontext_get_defer_bool(GenContext *context, Ast *defer)
{
assert(defer->ast_kind == AST_DEFER_STMT && defer->defer_stmt.emit_boolean);
if (!defer->defer_stmt.bool_var)
{
defer->defer_stmt.bool_var = gencontext_emit_alloca(context, llvm_type(type_bool), "defer");
}
return defer->defer_stmt.bool_var;
}
void gencontext_emit_defer(GenContext *context, Ast *defer_start, Ast *defer_end)
{
if (defer_start == defer_end) return;
Ast *defer = defer_start;
while (defer && defer != defer_end)
{
if (defer->defer_stmt.emit_boolean)
{
// We need at least the exit block and the "then" block.
LLVMBasicBlockRef exit_block = LLVMCreateBasicBlockInContext(context->context, "skip.defer");
LLVMBasicBlockRef defer_block = LLVMCreateBasicBlockInContext(context->context, "do.defer");
LLVMValueRef value = gencontext_emit_load(context, type_bool, gencontext_get_defer_bool(context, defer));
gencontext_emit_cond_br(context, value, defer_block, exit_block);
// Emit the defer.
gencontext_emit_block(context, defer_block);
gencontext_emit_stmt(context, defer->defer_stmt.body);
// Jump to exit.
gencontext_emit_br(context, exit_block);
// And now we just emit the exit block.
gencontext_emit_block(context, exit_block);
}
else
{
gencontext_emit_stmt(context, defer->defer_stmt.body);
}
defer = defer->defer_stmt.prev_defer;
}
}
void gencontext_emit_goto(GenContext *context, Ast *ast)
{
gencontext_emit_defer(context, ast->goto_stmt.defer.start, ast->goto_stmt.defer.end);
Ast *defer = ast->goto_stmt.label->label_stmt.defer;
while (defer != ast->goto_stmt.defer.end)
{
LLVMBuildStore(context->builder, LLVMConstInt(llvm_type(type_bool), 0, false),
gencontext_get_defer_bool(context, defer));
defer = defer->defer_stmt.prev_defer;
}
gencontext_emit_jmp(context, ast->goto_stmt.label->label_stmt.backend_value);
}
void gencontext_emit_break(GenContext *context, Ast *ast)
{
gencontext_emit_defer(context, ast->break_stmt.defers.start, ast->break_stmt.defers.end);
assert(context->break_continue_stack_index);
gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].break_block);
}
void gencontext_emit_continue(GenContext *context, Ast *ast)
{
gencontext_emit_defer(context, ast->continue_stmt.defers.start, ast->continue_stmt.defers.end);
assert(context->break_continue_stack_index);
gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].continue_block);
}
void gencontext_emit_next_stmt(GenContext *context, Ast *ast)
{
gencontext_emit_defer(context, ast->next_stmt.defers.start, ast->next_stmt.defers.end);
assert(context->break_continue_stack_index);
gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].next_block);
}
void gencontext_emit_scoped_stmt(GenContext *context, Ast *ast)
{
gencontext_emit_stmt(context, ast->scoped_stmt.stmt);
gencontext_emit_defer(context, ast->scoped_stmt.defers.start, ast->scoped_stmt.defers.end);
}
void gencontext_generate_catch_block_if_needed(GenContext *context, Ast *ast)
{
LLVMBasicBlockRef block = ast->catch_stmt.block;
if (block) return;
block = gencontext_create_free_block(context, "catchblock");
ast->catch_stmt.block = block;
LLVMTypeRef type;
if (ast->catch_stmt.error_param->type == type_error_union)
{
type = llvm_type(type_error_union);
}
else
{
type = llvm_type(type_error_base);
}
ast->catch_stmt.error_param->ref = gencontext_emit_alloca(context, type, "");
}
void gencontext_emit_catch_stmt(GenContext *context, Ast *ast)
{
gencontext_generate_catch_block_if_needed(context, ast);
LLVMBasicBlockRef after_catch = gencontext_create_free_block(context, "after_catch");
gencontext_emit_br(context, after_catch);
gencontext_emit_block(context, ast->catch_stmt.block);
gencontext_emit_stmt(context, ast->catch_stmt.body);
gencontext_emit_br(context, after_catch);
gencontext_emit_block(context, after_catch);
}
void gencontext_emit_panic_on_true(GenContext *context, LLVMValueRef value, const char *panic_name)
{
LLVMBasicBlockRef panic_block = gencontext_create_free_block(context, "panic");
LLVMBasicBlockRef ok_block = gencontext_create_free_block(context, "checkok");
gencontext_emit_cond_br(context, value, panic_block, ok_block);
gencontext_emit_block(context, panic_block);
gencontext_emit_call_intrinsic(context, trap_intrinsic_id, NULL, NULL, 0);
gencontext_emit_br(context, ok_block);
gencontext_emit_block(context, ok_block);
}
void gencontext_emit_stmt(GenContext *context, Ast *ast)
{
switch (ast->ast_kind)
{
case AST_POISONED:
UNREACHABLE
case AST_SCOPED_STMT:
gencontext_emit_scoped_stmt(context, ast);
break;
case AST_EXPR_STMT:
gencontext_emit_expr(context, ast->expr_stmt);
break;
case AST_DECLARE_STMT:
gencontext_emit_decl(context, ast);
break;
case AST_BREAK_STMT:
gencontext_emit_break(context, ast);
break;
case AST_CONTINUE_STMT:
gencontext_emit_continue(context, ast);
break;
case AST_IF_STMT:
gencontext_emit_if(context, ast);
break;
case AST_RETURN_STMT:
gencontext_emit_return(context, ast);
break;
case AST_COMPOUND_STMT:
gencontext_emit_compound_stmt(context, ast);
break;
case AST_FOR_STMT:
gencontext_emit_for_stmt(context, ast);
break;
case AST_WHILE_STMT:
gencontext_emit_while_stmt(context, ast);
break;
case AST_DO_STMT:
gencontext_emit_do_stmt(context, ast);
break;
case AST_NEXT_STMT:
gencontext_emit_next_stmt(context, ast);
break;
case AST_DEFER_STMT:
if (ast->defer_stmt.emit_boolean)
{
LLVMBuildStore(context->builder, LLVMConstInt(llvm_type(type_bool), 1, false),
gencontext_get_defer_bool(context, ast));
}
break;
case AST_NOP_STMT:
break;
case AST_CATCH_STMT:
gencontext_emit_catch_stmt(context, ast);
break;
case AST_THROW_STMT:
gencontext_emit_throw(context, ast);
break;
case AST_ASM_STMT:
TODO
case AST_CT_IF_STMT:
case AST_CT_ELIF_STMT:
case AST_CT_ELSE_STMT:
case AST_CT_FOR_STMT:
case AST_CT_SWITCH_STMT:
case AST_CT_DEFAULT_STMT:
case AST_CT_CASE_STMT:
case AST_GENERIC_CASE_STMT:
case AST_GENERIC_DEFAULT_STMT:
case AST_DECL_EXPR_LIST:
UNREACHABLE
case AST_LABEL:
gencontext_emit_label(context, ast);
break;
case AST_GOTO_STMT:
gencontext_emit_goto(context, ast);
break;
case AST_SWITCH_STMT:
gencontext_emit_switch(context, ast);
break;
case AST_CASE_STMT:
case AST_DEFAULT_STMT:
TODO
case AST_VOLATILE_STMT:
TODO
}
}