// // Created by servostar on 5/28/24. // #include #include #include #include #include #include #include #include #include BackendError impl_param_load( LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, const StorageExpr *expr, LLVMValueRef* storage_target) { BackendError err = SUCCESS; if (expr->impl.parameter->impl.declaration.qualifier == Out || expr->impl.parameter->impl.declaration.qualifier == InOut) { LLVMTypeRef llvm_type = NULL; err = get_type_impl(unit, scope->func_scope->global_scope, expr->impl.parameter->impl.declaration.type, &llvm_type); if (err.kind != Success) { return err; } *storage_target = LLVMBuildLoad2(builder, llvm_type, *storage_target, "strg.param.out.load"); } return err; } BackendError impl_storage_expr( LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, const StorageExpr *expr, LLVMValueRef* storage_target) { BackendError err = SUCCESS; switch (expr->kind) { case StorageExprKindVariable: *storage_target = get_variable(scope, expr->impl.variable->name); break; case StorageExprKindParameter: *storage_target = get_parameter(scope->func_scope, expr->impl.parameter->name); break; case StorageExprKindDereference: LLVMValueRef index = NULL; err = impl_expr(unit, scope, builder, expr->impl.dereference.index, false, &index); if (err.kind != Success) { return err; } LLVMValueRef array = NULL; err = impl_storage_expr(unit, builder, scope, expr->impl.dereference.array, &array); if (err.kind != Success) { return err; } if (expr->impl.dereference.array->kind == StorageExprKindParameter) { err = impl_param_load(unit, builder, scope, expr->impl.dereference.array, &array); if (err.kind != Success) { return err; } } if (expr->impl.dereference.array->kind == StorageExprKindDereference) { LLVMTypeRef deref_type = NULL; err = get_type_impl(unit, scope->func_scope->global_scope, expr->impl.dereference.array->target_type, &deref_type); if (err.kind != Success) { return err; } array = LLVMBuildLoad2(builder, deref_type, array, "strg.deref.indirect-load"); } LLVMTypeRef deref_type = NULL; err = get_type_impl(unit, scope->func_scope->global_scope, expr->target_type, &deref_type); if (err.kind != Success) { return err; } *storage_target = LLVMBuildGEP2(builder, deref_type, array, &index, 1, "strg.deref"); break; case StorageExprKindBoxAccess: // TODO: resolve LLVMValueRef from BoxAccess break; } return err; } BackendError impl_assign_stmt( LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, const Assignment *assignment ) { BackendError err = SUCCESS; DEBUG("implementing assignment for variable: %p", assignment); LLVMValueRef llvm_value = NULL; err = impl_expr(unit, scope, builder, assignment->value, TRUE, &llvm_value); if (err.kind != Success) { return err; } LLVMValueRef llvm_array = NULL; err = impl_storage_expr(unit, builder, scope, assignment->destination, &llvm_array); if (err.kind != Success) { return err; } LLVMBuildStore(builder, llvm_value, llvm_array); return err; } BackendError impl_basic_block(LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, const Block *block, LLVMBasicBlockRef *llvm_start_block, LLVMBasicBlockRef *llvm_end_block) { DEBUG("implementing basic block..."); BackendError err = SUCCESS; LLVMLocalScope *block_scope = new_local_scope(scope); // append a new LLVM basic block *llvm_start_block = LLVMAppendBasicBlockInContext(unit->context, scope->func_scope->llvm_func, "stmt.block.start"); LLVMPositionBuilderAtEnd(builder, *llvm_start_block); LLVMBasicBlockRef end_previous_block = *llvm_start_block; for (size_t i = 0; i < block->statemnts->len; i++) { DEBUG("building block statement %d of %d", i, block->statemnts->len); Statement* stmt = g_array_index(block->statemnts, Statement*, i); LLVMBasicBlockRef llvm_next_start_block = NULL; LLVMBasicBlockRef llvm_next_end_block = NULL; err = impl_stmt(unit, builder, scope, stmt, &llvm_next_start_block, &llvm_next_end_block); if (err.kind != Success) { return err; } if (llvm_next_end_block != NULL) { LLVMPositionBuilderAtEnd(builder, end_previous_block); LLVMBuildBr(builder, llvm_next_start_block); LLVMPositionBuilderAtEnd(builder, llvm_next_end_block); end_previous_block = llvm_next_end_block; } } *llvm_end_block = end_previous_block; delete_local_scope(block_scope); return err; } BackendError impl_while(LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, LLVMBasicBlockRef* llvm_start_block, LLVMBasicBlockRef* llvm_end_block, const While *while_stmt) { DEBUG("implementing while..."); BackendError err; // Create condition block LLVMBasicBlockRef while_cond_block = LLVMAppendBasicBlockInContext(unit->context, scope->func_scope->llvm_func, "loop.while.cond"); *llvm_start_block = while_cond_block; LLVMPositionBuilderAtEnd(builder, while_cond_block); // Resolve condition in block to a variable LLVMValueRef cond_result = NULL; err = impl_expr(unit, scope, builder, (Expression *) while_stmt->conditon, FALSE, &cond_result); if (err.kind != Success) { return err; } // build body of loop LLVMBasicBlockRef while_start_body_block = NULL; LLVMBasicBlockRef while_end_body_block = NULL; err = impl_basic_block(unit, builder, scope, &while_stmt->block, &while_start_body_block, &while_end_body_block); if (err.kind != Success) { return err; } LLVMPositionBuilderAtEnd(builder, while_end_body_block); // jump back to condition after body end LLVMBuildBr(builder, while_cond_block); // builder will continue after the loop LLVMBasicBlockRef while_after_block = LLVMAppendBasicBlockInContext(unit->context, scope->func_scope->llvm_func, "loop.while.after"); // build conditional branch at end of condition block LLVMPositionBuilderAtEnd(builder, while_cond_block); LLVMBuildCondBr(builder, cond_result, while_start_body_block, while_after_block); LLVMPositionBuilderAtEnd(builder, while_after_block); *llvm_end_block = while_after_block; return err; } gboolean is_parameter_out(Parameter *param) { gboolean is_out = FALSE; if (param->kind == ParameterDeclarationKind) { is_out = param->impl.declaration.qualifier == Out || param->impl.declaration.qualifier == InOut; } else { is_out = param->impl.definiton.declaration.qualifier == Out || param->impl.definiton.declaration.qualifier == InOut; } return is_out; } BackendError impl_func_call(LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, const FunctionCall *call) { DEBUG("implementing function call..."); BackendError err = SUCCESS; LLVMValueRef* arguments = mem_alloc(MemoryNamespaceLlvm, sizeof(LLVMValueRef) * call->expressions->len); for (size_t i = 0; i < call->expressions->len; i++) { Expression *arg = g_array_index(call->expressions, Expression*, i); GArray* param_list; if (call->function->kind == FunctionDeclarationKind) { param_list = call->function->impl.definition.parameter; } else { param_list = call->function->impl.declaration.parameter; } Parameter param = g_array_index(param_list, Parameter, i); LLVMValueRef llvm_arg = NULL; err = impl_expr(unit, scope, builder, arg, is_parameter_out(¶m), &llvm_arg); if (err.kind != Success) { break; } if (is_parameter_out(¶m)) { if ((arg->kind == ExpressionKindParameter && !is_parameter_out(arg->impl.parameter)) || arg->kind != ExpressionKindParameter) { LLVMValueRef index = LLVMConstInt(LLVMInt32Type(), 0, false); LLVMTypeRef llvm_type = NULL; get_type_impl(unit, scope->func_scope->global_scope, param.impl.declaration.type, &llvm_type); llvm_arg = LLVMBuildGEP2(builder, llvm_type, llvm_arg, &index, 1, ""); } } arguments[i] = llvm_arg; } if (err.kind == Success) { LLVMValueRef llvm_func = LLVMGetNamedFunction(unit->module, call->function->name); if (llvm_func == NULL) { return new_backend_impl_error(Implementation, NULL, "no declared function"); } LLVMTypeRef llvm_func_type = g_hash_table_lookup(scope->func_scope->global_scope->functions, call->function->name); LLVMBuildCall2(builder, llvm_func_type, llvm_func, arguments, call->expressions->len, ""); } return err; } BackendError impl_cond_block(LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, Expression *cond, const Block *block, LLVMBasicBlockRef *cond_block, LLVMBasicBlockRef *start_body_block, LLVMBasicBlockRef *end_body_block, LLVMValueRef *llvm_cond) { BackendError err; *cond_block = LLVMAppendBasicBlockInContext(unit->context, scope->func_scope->llvm_func, "stmt.branch.cond"); LLVMPositionBuilderAtEnd(builder, *cond_block); // Resolve condition in block to a variable err = impl_expr(unit, scope, builder, cond, FALSE, llvm_cond); if (err.kind == Success) { // build body of loop err = impl_basic_block(unit, builder, scope, block, start_body_block, end_body_block); } return err; } BackendError impl_branch(LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, LLVMBasicBlockRef* branch_start_block, LLVMBasicBlockRef* branch_end_block, const Branch *branch) { BackendError err = SUCCESS; GArray *cond_blocks = g_array_new(FALSE, FALSE, sizeof(LLVMBasicBlockRef)); GArray *start_body_blocks = g_array_new(FALSE, FALSE, sizeof(LLVMBasicBlockRef)); GArray *end_body_blocks = g_array_new(FALSE, FALSE, sizeof(LLVMBasicBlockRef)); GArray *cond_values = g_array_new(FALSE, FALSE, sizeof(LLVMValueRef)); // add If to arrays { LLVMBasicBlockRef cond_block = NULL; LLVMBasicBlockRef start_body_block = NULL; LLVMBasicBlockRef end_body_block = NULL; LLVMValueRef cond_value = NULL; err = impl_cond_block(unit, builder, scope, branch->ifBranch.conditon, &branch->ifBranch.block, &cond_block, &start_body_block, &end_body_block, &cond_value); g_array_append_val(cond_blocks, cond_block); g_array_append_val(start_body_blocks, start_body_block); g_array_append_val(end_body_blocks, end_body_block); g_array_append_val(cond_values, cond_value); } // generate else if(s) if (branch->elseIfBranches != NULL) { for (size_t i = 0; i < branch->elseIfBranches->len; i++) { LLVMBasicBlockRef cond_block = NULL; LLVMBasicBlockRef start_body_block = NULL; LLVMBasicBlockRef end_body_block = NULL; LLVMValueRef cond_value = NULL; ElseIf *elseIf = ((ElseIf *) branch->elseIfBranches->data) + i; err = impl_cond_block(unit, builder, scope, elseIf->conditon, &elseIf->block, &cond_block, &start_body_block, &end_body_block, &cond_value); g_array_append_val(cond_blocks, cond_block); g_array_append_val(start_body_blocks, start_body_block); g_array_append_val(end_body_blocks, end_body_block); g_array_append_val(cond_values, cond_value); } } LLVMBasicBlockRef after_block = NULL; // else block if (branch->elseBranch.block.statemnts != NULL) { LLVMBasicBlockRef start_else_block = NULL; err = impl_basic_block(unit, builder, scope, &branch->elseBranch.block, &start_else_block, &after_block); g_array_append_val(cond_blocks, start_else_block); } if (after_block == NULL) { after_block = LLVMAppendBasicBlockInContext(unit->context, scope->func_scope->llvm_func, "stmt.branch.after"); } LLVMPositionBuilderAtEnd(builder, after_block); // in case no else block is present // make the after block the else if (branch->elseBranch.block.statemnts == NULL) { g_array_append_val(cond_blocks, after_block); } for (size_t i = 0; i < cond_blocks->len - 1; i++) { LLVMBasicBlockRef next_block = g_array_index(cond_blocks, LLVMBasicBlockRef, i + 1); LLVMBasicBlockRef cond_block = g_array_index(cond_blocks, LLVMBasicBlockRef, i); LLVMBasicBlockRef start_body_block = g_array_index(start_body_blocks, LLVMBasicBlockRef, i); LLVMBasicBlockRef end_body_block = g_array_index(end_body_blocks, LLVMBasicBlockRef, i); LLVMValueRef cond_value = g_array_index(cond_values, LLVMValueRef, i); LLVMPositionBuilderAtEnd(builder, cond_block); LLVMBuildCondBr(builder, cond_value, start_body_block, next_block); LLVMPositionBuilderAtEnd(builder, end_body_block); LLVMBuildBr(builder, after_block); } *branch_start_block = g_array_index(cond_blocks, LLVMBasicBlockRef, 0); *branch_end_block = after_block; g_array_free(cond_blocks, TRUE); g_array_free(start_body_blocks, TRUE); g_array_free(end_body_blocks, TRUE); g_array_free(cond_values, TRUE); return err; } BackendError impl_decl(LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, VariableDeclaration *decl, const char *name) { DEBUG("implementing local declaration: %s", name); BackendError err = SUCCESS; LLVMTypeRef llvm_type = NULL; err = get_type_impl(unit, scope->func_scope->global_scope, decl->type, &llvm_type); if (err.kind != Success) { return err; } DEBUG("creating local variable..."); LLVMValueRef local = LLVMBuildAlloca(builder, llvm_type, name); LLVMValueRef initial_value = NULL; err = get_type_default_value(unit, scope->func_scope->global_scope, decl->type, &initial_value); if (err.kind == Success) { DEBUG("setting default value..."); LLVMBuildStore(builder, initial_value, local); g_hash_table_insert(scope->vars, (gpointer) name, local); } else { ERROR("unable to initialize local variable: %s", err.impl.message); } return err; } BackendError impl_def(LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, VariableDefiniton *def, const char *name) { DEBUG("implementing local definition: %s", name); BackendError err = SUCCESS; LLVMTypeRef llvm_type = NULL; err = get_type_impl(unit, scope->func_scope->global_scope, def->declaration.type, &llvm_type); if (err.kind != Success) { return err; } LLVMValueRef initial_value = NULL; err = impl_expr(unit, scope, builder, def->initializer, FALSE, &initial_value); if (err.kind != Success) { return err; } DEBUG("creating local variable..."); LLVMValueRef local = LLVMBuildAlloca(builder, llvm_type, name); DEBUG("setting default value"); LLVMBuildStore(builder, initial_value, local); g_hash_table_insert(scope->vars, (gpointer) name, local); return err; } BackendError impl_var(LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, Variable *var) { BackendError err; switch (var->kind) { case VariableKindDeclaration: err = impl_decl(unit, builder, scope, &var->impl.declaration, var->name); break; case VariableKindDefinition: err = impl_def(unit, builder, scope, &var->impl.definiton, var->name); break; default: err = new_backend_impl_error(Implementation, NULL, "Unexpected variable kind in statement"); break; } return err; } BackendError impl_stmt(LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, Statement *stmt, LLVMBasicBlockRef* llvm_start_block, LLVMBasicBlockRef* llvm_end_block) { assert(stmt != NULL); DEBUG("implementing statement: %ld", stmt->kind); BackendError err; switch (stmt->kind) { case StatementKindAssignment: err = impl_assign_stmt(unit, builder, scope, &stmt->impl.assignment); break; case StatementKindBranch: err = impl_branch(unit, builder, scope, llvm_start_block, llvm_end_block, &stmt->impl.branch); break; case StatementKindDeclaration: case StatementKindDefinition: err = impl_var(unit, builder, scope, stmt->impl.variable); break; case StatementKindWhile: err = impl_while(unit, builder, scope, llvm_start_block, llvm_end_block, &stmt->impl.whileLoop); break; case StatementKindFunctionCall: err = impl_func_call(unit, builder, scope, &stmt->impl.call); break; default: err = new_backend_impl_error(Implementation, NULL, "Unexpected statement kind"); break; } return err; } BackendError impl_block(LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMFuncScope *scope, LLVMBasicBlockRef* llvm_start_block, LLVMBasicBlockRef* llvm_end_block, const Block *block) { DEBUG("Implementing function block..."); BackendError err = SUCCESS; LLVMLocalScope *function_entry_scope = malloc(sizeof(LLVMLocalScope)); function_entry_scope->func_scope = scope; function_entry_scope->vars = g_hash_table_new(g_str_hash, g_str_equal); function_entry_scope->parent_scope = NULL; err = impl_basic_block(unit, builder, function_entry_scope, block, llvm_start_block, llvm_end_block); delete_local_scope(function_entry_scope); return err; }