IR working for if statements
This commit is contained in:
parent
ce3c52471d
commit
ac6d1674e3
@ -1411,6 +1411,8 @@ struct IrBasicBlock {
|
||||
IrBasicBlock *other;
|
||||
const char *name_hint;
|
||||
size_t debug_id;
|
||||
size_t ref_count;
|
||||
LLVMBasicBlockRef llvm_block;
|
||||
};
|
||||
|
||||
enum IrInstructionId {
|
||||
@ -1428,6 +1430,9 @@ enum IrInstructionId {
|
||||
IrInstructionIdConst,
|
||||
IrInstructionIdReturn,
|
||||
IrInstructionIdCast,
|
||||
IrInstructionIdContainerInitList,
|
||||
IrInstructionIdContainerInitFields,
|
||||
IrInstructionIdUnreachable,
|
||||
};
|
||||
|
||||
struct IrInstruction {
|
||||
@ -1586,4 +1591,25 @@ struct IrInstructionCast {
|
||||
LLVMValueRef tmp_ptr;
|
||||
};
|
||||
|
||||
struct IrInstructionContainerInitList {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *container_type;
|
||||
size_t item_count;
|
||||
IrInstruction **items;
|
||||
};
|
||||
|
||||
struct IrInstructionContainerInitFields {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *container_type;
|
||||
size_t field_count;
|
||||
Buf **field_names;
|
||||
IrInstruction **field_values;
|
||||
};
|
||||
|
||||
struct IrInstructionUnreachable {
|
||||
IrInstruction base;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
190
src/analyze.cpp
190
src/analyze.cpp
@ -29,7 +29,6 @@ static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *
|
||||
BlockContext *context, AstNode *node, Buf *err_name);
|
||||
static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
TypeTableEntry *expected_type, AstNode *node);
|
||||
static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node);
|
||||
static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn,
|
||||
bool depends_on_compile_var);
|
||||
static TypeTableEntry *resolve_expr_const_val_as_generic_fn(CodeGen *g, AstNode *node,
|
||||
@ -2386,187 +2385,6 @@ static TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const char *err_container_init_syntax_name(ContainerInitKind kind) {
|
||||
switch (kind) {
|
||||
case ContainerInitKindStruct:
|
||||
return "struct";
|
||||
case ContainerInitKindArray:
|
||||
return "array";
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry *import,
|
||||
BlockContext *context, AstNode *node)
|
||||
{
|
||||
assert(node->type == NodeTypeContainerInitExpr);
|
||||
|
||||
AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr;
|
||||
|
||||
ContainerInitKind kind = container_init_expr->kind;
|
||||
|
||||
if (container_init_expr->type->type == NodeTypeFieldAccessExpr) {
|
||||
container_init_expr->type->data.field_access_expr.container_init_expr_node = node;
|
||||
}
|
||||
|
||||
TypeTableEntry *container_meta_type = analyze_expression(g, import, context, nullptr,
|
||||
container_init_expr->type);
|
||||
|
||||
if (container_meta_type->id == TypeTableEntryIdInvalid) {
|
||||
return g->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
if (node->data.container_init_expr.enum_type) {
|
||||
get_resolved_expr(node)->const_val = get_resolved_expr(container_init_expr->type)->const_val;
|
||||
return node->data.container_init_expr.enum_type;
|
||||
}
|
||||
|
||||
TypeTableEntry *container_type = resolve_type(g, container_init_expr->type);
|
||||
|
||||
if (container_type->id == TypeTableEntryIdInvalid) {
|
||||
return container_type;
|
||||
} else if (container_type->id == TypeTableEntryIdStruct &&
|
||||
!container_type->data.structure.is_slice &&
|
||||
(kind == ContainerInitKindStruct || (kind == ContainerInitKindArray &&
|
||||
container_init_expr->entries.length == 0)))
|
||||
{
|
||||
StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr;
|
||||
codegen->type_entry = container_type;
|
||||
codegen->source_node = node;
|
||||
|
||||
|
||||
size_t expr_field_count = container_init_expr->entries.length;
|
||||
size_t actual_field_count = container_type->data.structure.src_field_count;
|
||||
|
||||
AstNode *non_const_expr_culprit = nullptr;
|
||||
|
||||
size_t *field_use_counts = allocate<size_t>(actual_field_count);
|
||||
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
|
||||
const_val->ok = true;
|
||||
const_val->data.x_struct.fields = allocate<ConstExprValue*>(actual_field_count);
|
||||
for (size_t i = 0; i < expr_field_count; i += 1) {
|
||||
AstNode *val_field_node = container_init_expr->entries.at(i);
|
||||
assert(val_field_node->type == NodeTypeStructValueField);
|
||||
|
||||
val_field_node->block_context = context;
|
||||
|
||||
TypeStructField *type_field = find_struct_type_field(container_type,
|
||||
val_field_node->data.struct_val_field.name);
|
||||
|
||||
if (!type_field) {
|
||||
add_node_error(g, val_field_node,
|
||||
buf_sprintf("no member named '%s' in '%s'",
|
||||
buf_ptr(val_field_node->data.struct_val_field.name), buf_ptr(&container_type->name)));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type_field->type_entry->id == TypeTableEntryIdInvalid) {
|
||||
return g->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
size_t field_index = type_field->src_index;
|
||||
field_use_counts[field_index] += 1;
|
||||
if (field_use_counts[field_index] > 1) {
|
||||
add_node_error(g, val_field_node, buf_sprintf("duplicate field"));
|
||||
continue;
|
||||
}
|
||||
|
||||
val_field_node->data.struct_val_field.type_struct_field = type_field;
|
||||
|
||||
analyze_expression(g, import, context, type_field->type_entry,
|
||||
val_field_node->data.struct_val_field.expr);
|
||||
|
||||
if (const_val->ok) {
|
||||
ConstExprValue *field_val =
|
||||
&get_resolved_expr(val_field_node->data.struct_val_field.expr)->const_val;
|
||||
if (field_val->ok) {
|
||||
const_val->data.x_struct.fields[field_index] = field_val;
|
||||
const_val->depends_on_compile_var = const_val->depends_on_compile_var || field_val->depends_on_compile_var;
|
||||
} else {
|
||||
const_val->ok = false;
|
||||
non_const_expr_culprit = val_field_node->data.struct_val_field.expr;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!const_val->ok) {
|
||||
assert(non_const_expr_culprit);
|
||||
if (context->fn_entry) {
|
||||
context->fn_entry->struct_val_expr_alloca_list.append(codegen);
|
||||
} else {
|
||||
add_node_error(g, non_const_expr_culprit, buf_sprintf("unable to evaluate constant expression"));
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < actual_field_count; i += 1) {
|
||||
if (field_use_counts[i] == 0) {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i].name)));
|
||||
}
|
||||
}
|
||||
return container_type;
|
||||
} else if (container_type->id == TypeTableEntryIdStruct &&
|
||||
container_type->data.structure.is_slice &&
|
||||
kind == ContainerInitKindArray)
|
||||
{
|
||||
size_t elem_count = container_init_expr->entries.length;
|
||||
|
||||
TypeTableEntry *pointer_type = container_type->data.structure.fields[0].type_entry;
|
||||
assert(pointer_type->id == TypeTableEntryIdPointer);
|
||||
TypeTableEntry *child_type = pointer_type->data.pointer.child_type;
|
||||
|
||||
ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
|
||||
const_val->ok = true;
|
||||
const_val->data.x_array.fields = allocate<ConstExprValue*>(elem_count);
|
||||
|
||||
for (size_t i = 0; i < elem_count; i += 1) {
|
||||
AstNode **elem_node = &container_init_expr->entries.at(i);
|
||||
analyze_expression(g, import, context, child_type, *elem_node);
|
||||
|
||||
if (const_val->ok) {
|
||||
ConstExprValue *elem_const_val = &get_resolved_expr(*elem_node)->const_val;
|
||||
if (elem_const_val->ok) {
|
||||
const_val->data.x_array.fields[i] = elem_const_val;
|
||||
const_val->depends_on_compile_var = const_val->depends_on_compile_var ||
|
||||
elem_const_val->depends_on_compile_var;
|
||||
} else {
|
||||
const_val->ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TypeTableEntry *fixed_size_array_type = get_array_type(g, child_type, elem_count);
|
||||
|
||||
StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr;
|
||||
codegen->type_entry = fixed_size_array_type;
|
||||
codegen->source_node = node;
|
||||
if (!const_val->ok) {
|
||||
if (!context->fn_entry) {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("unable to evaluate constant expression"));
|
||||
} else {
|
||||
context->fn_entry->struct_val_expr_alloca_list.append(codegen);
|
||||
}
|
||||
}
|
||||
|
||||
return fixed_size_array_type;
|
||||
} else if (container_type->id == TypeTableEntryIdArray) {
|
||||
zig_panic("TODO array container init");
|
||||
return container_type;
|
||||
} else if (container_type->id == TypeTableEntryIdVoid) {
|
||||
if (container_init_expr->entries.length != 0) {
|
||||
add_node_error(g, node, buf_sprintf("void expression expects no arguments"));
|
||||
return g->builtin_types.entry_invalid;
|
||||
} else {
|
||||
return resolve_expr_const_val_as_void(g, node);
|
||||
}
|
||||
} else {
|
||||
add_node_error(g, node,
|
||||
buf_sprintf("type '%s' does not support %s initialization syntax",
|
||||
buf_ptr(&container_type->name), err_container_init_syntax_name(kind)));
|
||||
return g->builtin_types.entry_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_container(TypeTableEntry *type_entry) {
|
||||
switch (type_entry->id) {
|
||||
case TypeTableEntryIdInvalid:
|
||||
@ -2928,12 +2746,6 @@ static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *i
|
||||
return return_type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node) {
|
||||
Expr *expr = get_resolved_expr(node);
|
||||
expr->const_val.ok = true;
|
||||
return g->builtin_types.entry_void;
|
||||
}
|
||||
|
||||
static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type,
|
||||
bool depends_on_compile_var)
|
||||
{
|
||||
@ -5331,7 +5143,7 @@ static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEn
|
||||
return_type = analyze_field_access_expr(g, import, context, expected_type, node);
|
||||
break;
|
||||
case NodeTypeContainerInitExpr:
|
||||
return_type = analyze_container_init_expr(g, import, context, node);
|
||||
zig_panic("analyze container init moved to ir.cpp");
|
||||
break;
|
||||
case NodeTypeNumberLiteral:
|
||||
return_type = analyze_number_literal_expr(g, import, context, expected_type, node);
|
||||
|
397
src/codegen.cpp
397
src/codegen.cpp
@ -12,6 +12,7 @@
|
||||
#include "errmsg.hpp"
|
||||
#include "error.hpp"
|
||||
#include "hash_map.hpp"
|
||||
#include "ir.hpp"
|
||||
#include "link.hpp"
|
||||
#include "os.hpp"
|
||||
#include "parseh.hpp"
|
||||
@ -513,18 +514,6 @@ static LLVMValueRef gen_truncate(CodeGen *g, AstNode *node) {
|
||||
return LLVMBuildTrunc(g->builder, src_val, dest_type->type_ref, "");
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_unreachable(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeFnCallExpr);
|
||||
|
||||
if (want_debug_safety(g, node) || g->is_test_build) {
|
||||
gen_debug_safety_crash(g);
|
||||
} else {
|
||||
LLVMBuildUnreachable(g->builder);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeFnCallExpr);
|
||||
|
||||
@ -711,7 +700,7 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
|
||||
case BuiltinFnIdTruncate:
|
||||
return gen_truncate(g, node);
|
||||
case BuiltinFnIdUnreachable:
|
||||
return gen_unreachable(g, node);
|
||||
zig_panic("moved to ir render");
|
||||
case BuiltinFnIdSetFnTest:
|
||||
case BuiltinFnIdSetFnVisible:
|
||||
case BuiltinFnIdSetFnStaticEval:
|
||||
@ -1334,154 +1323,6 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, TypeTableEntry *type_entry,
|
||||
return result;
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypePrefixOpExpr);
|
||||
assert(node->data.prefix_op_expr.primary_expr);
|
||||
|
||||
AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
|
||||
TypeTableEntry *expr_type = get_expr_type(expr_node);
|
||||
|
||||
PrefixOp op = node->data.prefix_op_expr.prefix_op;
|
||||
|
||||
switch (op) {
|
||||
case PrefixOpInvalid:
|
||||
zig_unreachable();
|
||||
case PrefixOpNegation:
|
||||
case PrefixOpNegationWrap:
|
||||
{
|
||||
LLVMValueRef expr = gen_expr(g, expr_node);
|
||||
if (expr_type->id == TypeTableEntryIdFloat) {
|
||||
return LLVMBuildFNeg(g->builder, expr, "");
|
||||
} else if (expr_type->id == TypeTableEntryIdInt) {
|
||||
if (op == PrefixOpNegationWrap) {
|
||||
return LLVMBuildNeg(g->builder, expr, "");
|
||||
} else if (want_debug_safety(g, expr_node)) {
|
||||
LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr));
|
||||
return gen_overflow_op(g, expr_type, AddSubMulSub, zero, expr);
|
||||
} else if (expr_type->data.integral.is_signed) {
|
||||
return LLVMBuildNSWNeg(g->builder, expr, "");
|
||||
} else {
|
||||
return LLVMBuildNUWNeg(g->builder, expr, "");
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
case PrefixOpBoolNot:
|
||||
{
|
||||
LLVMValueRef expr = gen_expr(g, expr_node);
|
||||
LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr));
|
||||
return LLVMBuildICmp(g->builder, LLVMIntEQ, expr, zero, "");
|
||||
}
|
||||
case PrefixOpBinNot:
|
||||
{
|
||||
LLVMValueRef expr = gen_expr(g, expr_node);
|
||||
return LLVMBuildNot(g->builder, expr, "");
|
||||
}
|
||||
case PrefixOpAddressOf:
|
||||
case PrefixOpConstAddressOf:
|
||||
{
|
||||
TypeTableEntry *lvalue_type;
|
||||
return gen_lvalue(g, node, expr_node, &lvalue_type);
|
||||
}
|
||||
|
||||
case PrefixOpDereference:
|
||||
{
|
||||
LLVMValueRef expr = gen_expr(g, expr_node);
|
||||
assert(expr_type->id == TypeTableEntryIdPointer);
|
||||
if (!type_has_bits(expr_type)) {
|
||||
return nullptr;
|
||||
} else {
|
||||
TypeTableEntry *child_type = expr_type->data.pointer.child_type;
|
||||
return get_handle_value(g, expr, child_type);
|
||||
}
|
||||
}
|
||||
case PrefixOpMaybe:
|
||||
{
|
||||
zig_panic("TODO codegen PrefixOpMaybe");
|
||||
}
|
||||
case PrefixOpError:
|
||||
{
|
||||
zig_panic("TODO codegen PrefixOpError");
|
||||
}
|
||||
case PrefixOpUnwrapError:
|
||||
{
|
||||
LLVMValueRef expr_val = gen_expr(g, expr_node);
|
||||
TypeTableEntry *expr_type = get_expr_type(expr_node);
|
||||
assert(expr_type->id == TypeTableEntryIdErrorUnion);
|
||||
TypeTableEntry *child_type = expr_type->data.error.child_type;
|
||||
|
||||
if (want_debug_safety(g, node)) {
|
||||
LLVMValueRef err_val;
|
||||
if (type_has_bits(child_type)) {
|
||||
LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, "");
|
||||
err_val = LLVMBuildLoad(g->builder, err_val_ptr, "");
|
||||
} else {
|
||||
err_val = expr_val;
|
||||
}
|
||||
LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref);
|
||||
LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, "");
|
||||
LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrError");
|
||||
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrOk");
|
||||
LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, err_block);
|
||||
gen_debug_safety_crash(g);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||
}
|
||||
|
||||
if (type_has_bits(child_type)) {
|
||||
LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, "");
|
||||
return get_handle_value(g, child_val_ptr, child_type);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
case PrefixOpUnwrapMaybe:
|
||||
{
|
||||
LLVMValueRef expr_val = gen_expr(g, expr_node);
|
||||
|
||||
TypeTableEntry *expr_type = get_expr_type(expr_node);
|
||||
assert(expr_type->id == TypeTableEntryIdMaybe);
|
||||
TypeTableEntry *child_type = expr_type->data.maybe.child_type;
|
||||
|
||||
if (want_debug_safety(g, node)) {
|
||||
LLVMValueRef cond_val;
|
||||
if (child_type->id == TypeTableEntryIdPointer ||
|
||||
child_type->id == TypeTableEntryIdFn)
|
||||
{
|
||||
cond_val = LLVMBuildICmp(g->builder, LLVMIntNE, expr_val,
|
||||
LLVMConstNull(child_type->type_ref), "");
|
||||
} else {
|
||||
LLVMValueRef maybe_null_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, "");
|
||||
cond_val = LLVMBuildLoad(g->builder, maybe_null_ptr, "");
|
||||
}
|
||||
|
||||
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapMaybeOk");
|
||||
LLVMBasicBlockRef null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapMaybeNull");
|
||||
LLVMBuildCondBr(g->builder, cond_val, ok_block, null_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, null_block);
|
||||
gen_debug_safety_crash(g);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||
}
|
||||
|
||||
|
||||
if (child_type->id == TypeTableEntryIdPointer ||
|
||||
child_type->id == TypeTableEntryIdFn)
|
||||
{
|
||||
return expr_val;
|
||||
} else {
|
||||
LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, "");
|
||||
return get_handle_value(g, maybe_field_ptr, child_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2,
|
||||
TypeTableEntry *type_entry, bool exact)
|
||||
{
|
||||
@ -2468,8 +2309,20 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {
|
||||
if (!type_has_bits(instruction->type_entry))
|
||||
return nullptr;
|
||||
if (!instruction->llvm_value) {
|
||||
assert(instruction->static_value.ok);
|
||||
assert(instruction->type_entry);
|
||||
instruction->llvm_value = gen_const_val(g, instruction->type_entry, &instruction->static_value);
|
||||
assert(instruction->llvm_value);
|
||||
}
|
||||
return instruction->llvm_value;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) {
|
||||
LLVMBuildRet(g->builder, return_instruction->value->llvm_value);
|
||||
LLVMBuildRet(g->builder, ir_llvm_value(g, return_instruction->value));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -2488,8 +2341,8 @@ static LLVMValueRef ir_render_bin_op_bool(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionBinOp *bin_op_instruction)
|
||||
{
|
||||
IrBinOp op_id = bin_op_instruction->op_id;
|
||||
LLVMValueRef op1 = bin_op_instruction->op1->llvm_value;
|
||||
LLVMValueRef op2 = bin_op_instruction->op2->llvm_value;
|
||||
LLVMValueRef op1 = ir_llvm_value(g, bin_op_instruction->op1);
|
||||
LLVMValueRef op2 = ir_llvm_value(g, bin_op_instruction->op2);
|
||||
if (op_id == IrBinOpBoolOr) {
|
||||
return LLVMBuildOr(g->builder, op1, op2, "");
|
||||
} else if (op_id == IrBinOpBoolAnd) {
|
||||
@ -2508,18 +2361,21 @@ static LLVMValueRef ir_render_bin_op_add(CodeGen *g, IrExecutable *executable,
|
||||
|
||||
assert(op1->type_entry == op2->type_entry);
|
||||
|
||||
LLVMValueRef op1_value = ir_llvm_value(g, op1);
|
||||
LLVMValueRef op2_value = ir_llvm_value(g, op2);
|
||||
|
||||
if (op1->type_entry->id == TypeTableEntryIdFloat) {
|
||||
return LLVMBuildFAdd(g->builder, op1->llvm_value, op2->llvm_value, "");
|
||||
return LLVMBuildFAdd(g->builder, op1_value, op2_value, "");
|
||||
} else if (op1->type_entry->id == TypeTableEntryIdInt) {
|
||||
bool is_wrapping = (op_id == IrBinOpAddWrap);
|
||||
if (is_wrapping) {
|
||||
return LLVMBuildAdd(g->builder, op1->llvm_value, op2->llvm_value, "");
|
||||
return LLVMBuildAdd(g->builder, op1_value, op2_value, "");
|
||||
} else if (ir_want_debug_safety(g, &bin_op_instruction->base)) {
|
||||
return gen_overflow_op(g, op1->type_entry, AddSubMulAdd, op1->llvm_value, op2->llvm_value);
|
||||
return gen_overflow_op(g, op1->type_entry, AddSubMulAdd, op1_value, op2_value);
|
||||
} else if (op1->type_entry->data.integral.is_signed) {
|
||||
return LLVMBuildNSWAdd(g->builder, op1->llvm_value, op2->llvm_value, "");
|
||||
return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, "");
|
||||
} else {
|
||||
return LLVMBuildNUWAdd(g->builder, op1->llvm_value, op2->llvm_value, "");
|
||||
return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
@ -2570,7 +2426,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
|
||||
{
|
||||
TypeTableEntry *actual_type = cast_instruction->value->type_entry;
|
||||
TypeTableEntry *wanted_type = cast_instruction->base.type_entry;
|
||||
LLVMValueRef expr_val = cast_instruction->value->llvm_value;
|
||||
LLVMValueRef expr_val = ir_llvm_value(g, cast_instruction->value);
|
||||
assert(expr_val);
|
||||
|
||||
switch (cast_instruction->cast_op) {
|
||||
@ -2792,13 +2648,176 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_unreachable(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionUnreachable *unreachable_instruction)
|
||||
{
|
||||
if (ir_want_debug_safety(g, &unreachable_instruction->base) || g->is_test_build) {
|
||||
gen_debug_safety_crash(g);
|
||||
} else {
|
||||
LLVMBuildUnreachable(g->builder);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_cond_br(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionCondBr *cond_br_instruction)
|
||||
{
|
||||
LLVMBuildCondBr(g->builder,
|
||||
ir_llvm_value(g, cond_br_instruction->condition),
|
||||
cond_br_instruction->then_block->llvm_block,
|
||||
cond_br_instruction->else_block->llvm_block);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_br(CodeGen *g, IrExecutable *executable, IrInstructionBr *br_instruction) {
|
||||
LLVMBuildBr(g->builder, br_instruction->dest_block->llvm_block);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInstructionUnOp *un_op_instruction) {
|
||||
IrUnOp op_id = un_op_instruction->op_id;
|
||||
LLVMValueRef expr = ir_llvm_value(g, un_op_instruction->value);
|
||||
TypeTableEntry *expr_type = un_op_instruction->value->type_entry;
|
||||
|
||||
switch (op_id) {
|
||||
case IrUnOpInvalid:
|
||||
zig_unreachable();
|
||||
case IrUnOpNegation:
|
||||
case IrUnOpNegationWrap:
|
||||
{
|
||||
if (expr_type->id == TypeTableEntryIdFloat) {
|
||||
return LLVMBuildFNeg(g->builder, expr, "");
|
||||
} else if (expr_type->id == TypeTableEntryIdInt) {
|
||||
if (op_id == IrUnOpNegationWrap) {
|
||||
return LLVMBuildNeg(g->builder, expr, "");
|
||||
} else if (ir_want_debug_safety(g, &un_op_instruction->base)) {
|
||||
LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr));
|
||||
return gen_overflow_op(g, expr_type, AddSubMulSub, zero, expr);
|
||||
} else if (expr_type->data.integral.is_signed) {
|
||||
return LLVMBuildNSWNeg(g->builder, expr, "");
|
||||
} else {
|
||||
return LLVMBuildNUWNeg(g->builder, expr, "");
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
case IrUnOpBoolNot:
|
||||
{
|
||||
LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr));
|
||||
return LLVMBuildICmp(g->builder, LLVMIntEQ, expr, zero, "");
|
||||
}
|
||||
case IrUnOpBinNot:
|
||||
return LLVMBuildNot(g->builder, expr, "");
|
||||
case IrUnOpAddressOf:
|
||||
case IrUnOpConstAddressOf:
|
||||
zig_panic("TODO address of codegen");
|
||||
//{
|
||||
// TypeTableEntry *lvalue_type;
|
||||
// return gen_lvalue(g, node, expr_node, &lvalue_type);
|
||||
//}
|
||||
case IrUnOpDereference:
|
||||
{
|
||||
assert(expr_type->id == TypeTableEntryIdPointer);
|
||||
if (!type_has_bits(expr_type)) {
|
||||
return nullptr;
|
||||
} else {
|
||||
TypeTableEntry *child_type = expr_type->data.pointer.child_type;
|
||||
return get_handle_value(g, expr, child_type);
|
||||
}
|
||||
}
|
||||
case IrUnOpError:
|
||||
{
|
||||
zig_panic("TODO codegen PrefixOpError");
|
||||
}
|
||||
case IrUnOpMaybe:
|
||||
{
|
||||
zig_panic("TODO codegen PrefixOpMaybe");
|
||||
}
|
||||
case IrUnOpUnwrapError:
|
||||
{
|
||||
assert(expr_type->id == TypeTableEntryIdErrorUnion);
|
||||
TypeTableEntry *child_type = expr_type->data.error.child_type;
|
||||
|
||||
if (ir_want_debug_safety(g, &un_op_instruction->base)) {
|
||||
LLVMValueRef err_val;
|
||||
if (type_has_bits(child_type)) {
|
||||
LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, expr, 0, "");
|
||||
err_val = LLVMBuildLoad(g->builder, err_val_ptr, "");
|
||||
} else {
|
||||
err_val = expr;
|
||||
}
|
||||
LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref);
|
||||
LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, "");
|
||||
LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrError");
|
||||
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrOk");
|
||||
LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, err_block);
|
||||
gen_debug_safety_crash(g);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||
}
|
||||
|
||||
if (type_has_bits(child_type)) {
|
||||
LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr, 1, "");
|
||||
return get_handle_value(g, child_val_ptr, child_type);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
case IrUnOpUnwrapMaybe:
|
||||
{
|
||||
assert(expr_type->id == TypeTableEntryIdMaybe);
|
||||
TypeTableEntry *child_type = expr_type->data.maybe.child_type;
|
||||
|
||||
if (ir_want_debug_safety(g, &un_op_instruction->base)) {
|
||||
LLVMValueRef cond_val;
|
||||
if (child_type->id == TypeTableEntryIdPointer ||
|
||||
child_type->id == TypeTableEntryIdFn)
|
||||
{
|
||||
cond_val = LLVMBuildICmp(g->builder, LLVMIntNE, expr,
|
||||
LLVMConstNull(child_type->type_ref), "");
|
||||
} else {
|
||||
LLVMValueRef maybe_null_ptr = LLVMBuildStructGEP(g->builder, expr, 1, "");
|
||||
cond_val = LLVMBuildLoad(g->builder, maybe_null_ptr, "");
|
||||
}
|
||||
|
||||
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapMaybeOk");
|
||||
LLVMBasicBlockRef null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapMaybeNull");
|
||||
LLVMBuildCondBr(g->builder, cond_val, ok_block, null_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, null_block);
|
||||
gen_debug_safety_crash(g);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||
}
|
||||
|
||||
|
||||
if (child_type->id == TypeTableEntryIdPointer ||
|
||||
child_type->id == TypeTableEntryIdFn)
|
||||
{
|
||||
return expr;
|
||||
} else {
|
||||
LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, expr, 0, "");
|
||||
return get_handle_value(g, maybe_field_ptr, child_type);
|
||||
}
|
||||
}
|
||||
case IrUnOpErrorReturn:
|
||||
case IrUnOpMaybeReturn:
|
||||
zig_panic("TODO codegen more un ops");
|
||||
}
|
||||
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, IrInstruction *instruction) {
|
||||
set_debug_source_node(g, instruction->source_node);
|
||||
|
||||
switch (instruction->id) {
|
||||
case IrInstructionIdInvalid:
|
||||
zig_unreachable();
|
||||
case IrInstructionIdConst:
|
||||
return gen_const_val(g, instruction->type_entry, &instruction->static_value);
|
||||
zig_unreachable();
|
||||
case IrInstructionIdReturn:
|
||||
return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
|
||||
case IrInstructionIdLoadVar:
|
||||
@ -2807,14 +2826,21 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
return ir_render_bin_op(g, executable, (IrInstructionBinOp *)instruction);
|
||||
case IrInstructionIdCast:
|
||||
return ir_render_cast(g, executable, (IrInstructionCast *)instruction);
|
||||
case IrInstructionIdUnreachable:
|
||||
return ir_render_unreachable(g, executable, (IrInstructionUnreachable *)instruction);
|
||||
case IrInstructionIdCondBr:
|
||||
return ir_render_cond_br(g, executable, (IrInstructionCondBr *)instruction);
|
||||
case IrInstructionIdBr:
|
||||
return ir_render_br(g, executable, (IrInstructionBr *)instruction);
|
||||
case IrInstructionIdUnOp:
|
||||
return ir_render_un_op(g, executable, (IrInstructionUnOp *)instruction);
|
||||
case IrInstructionIdSwitchBr:
|
||||
case IrInstructionIdPhi:
|
||||
case IrInstructionIdStoreVar:
|
||||
case IrInstructionIdCall:
|
||||
case IrInstructionIdBuiltinCall:
|
||||
case IrInstructionIdUnOp:
|
||||
case IrInstructionIdContainerInitList:
|
||||
case IrInstructionIdContainerInitFields:
|
||||
zig_panic("TODO render more IR instructions to LLVM");
|
||||
}
|
||||
zig_unreachable();
|
||||
@ -2826,8 +2852,14 @@ static void ir_render(CodeGen *g, FnTableEntry *fn_entry) {
|
||||
assert(executable->basic_block_list.length > 0);
|
||||
for (size_t block_i = 0; block_i < executable->basic_block_list.length; block_i += 1) {
|
||||
IrBasicBlock *current_block = executable->basic_block_list.at(block_i);
|
||||
if (current_block->ref_count == 0)
|
||||
continue;
|
||||
assert(current_block->llvm_block);
|
||||
LLVMPositionBuilderAtEnd(g->builder, current_block->llvm_block);
|
||||
for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) {
|
||||
IrInstruction *instruction = current_block->instruction_list.at(instr_i);
|
||||
if (instruction->ref_count == 0 && !ir_has_side_effects(instruction))
|
||||
continue;
|
||||
instruction->llvm_value = ir_render_instruction(g, executable, instruction);
|
||||
}
|
||||
}
|
||||
@ -3622,7 +3654,7 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
|
||||
case NodeTypeVariableDeclaration:
|
||||
return gen_var_decl_expr(g, node);
|
||||
case NodeTypePrefixOpExpr:
|
||||
return gen_prefix_op_expr(g, node);
|
||||
zig_panic("moved to ir render");
|
||||
case NodeTypeFnCallExpr:
|
||||
return gen_fn_call_expr(g, node);
|
||||
case NodeTypeArrayAccessExpr:
|
||||
@ -4018,14 +4050,15 @@ static void generate_error_name_table(CodeGen *g) {
|
||||
LLVMSetUnnamedAddr(g->err_name_table, true);
|
||||
}
|
||||
|
||||
static void build_label_blocks(CodeGen *g, FnTableEntry *fn) {
|
||||
LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn->fn_value, "entry");
|
||||
for (size_t i = 0; i < fn->all_labels.length; i += 1) {
|
||||
LabelTableEntry *label = fn->all_labels.at(i);
|
||||
Buf *name = label->decl_node->data.label.name;
|
||||
label->basic_block = LLVMAppendBasicBlock(fn->fn_value, buf_ptr(name));
|
||||
static void build_all_basic_blocks(CodeGen *g, FnTableEntry *fn) {
|
||||
IrExecutable *executable = &fn->analyzed_executable;
|
||||
assert(executable->basic_block_list.length > 0);
|
||||
for (size_t block_i = 0; block_i < executable->basic_block_list.length; block_i += 1) {
|
||||
IrBasicBlock *bb = executable->basic_block_list.at(block_i);
|
||||
bb->llvm_block = LLVMAppendBasicBlock(fn->fn_value, bb->name_hint);
|
||||
}
|
||||
LLVMPositionBuilderAtEnd(g->builder, entry_block);
|
||||
IrBasicBlock *entry_bb = executable->basic_block_list.at(0);
|
||||
LLVMPositionBuilderAtEnd(g->builder, entry_bb->llvm_block);
|
||||
}
|
||||
|
||||
static void gen_global_var(CodeGen *g, VariableTableEntry *var, LLVMValueRef init_val,
|
||||
@ -4252,7 +4285,7 @@ static void do_code_gen(CodeGen *g) {
|
||||
assert(proto_node->type == NodeTypeFnProto);
|
||||
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
|
||||
|
||||
build_label_blocks(g, fn_table_entry);
|
||||
build_all_basic_blocks(g, fn_table_entry);
|
||||
|
||||
|
||||
// Set up debug info for blocks
|
||||
|
711
src/ir.cpp
711
src/ir.cpp
@ -40,6 +40,29 @@ static size_t exec_next_debug_id(IrExecutable *exec) {
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ir_link_new_instruction(IrInstruction *new_instruction, IrInstruction *old_instruction) {
|
||||
new_instruction->other = old_instruction;
|
||||
old_instruction->other = new_instruction;
|
||||
}
|
||||
|
||||
static void ir_link_new_bb(IrBasicBlock *new_bb, IrBasicBlock *old_bb) {
|
||||
new_bb->other = old_bb;
|
||||
old_bb->other = new_bb;
|
||||
}
|
||||
|
||||
static void ir_ref_bb(IrBasicBlock *bb) {
|
||||
bb->ref_count += 1;
|
||||
}
|
||||
|
||||
static void ir_unref_bb(IrBasicBlock *bb) {
|
||||
bb->ref_count -= 1;
|
||||
assert(bb->ref_count != SIZE_MAX);
|
||||
}
|
||||
|
||||
static void ir_ref_instruction(IrInstruction *instruction) {
|
||||
instruction->ref_count += 1;
|
||||
}
|
||||
|
||||
static IrBasicBlock *ir_build_basic_block(IrBuilder *irb, const char *name_hint) {
|
||||
IrBasicBlock *result = allocate<IrBasicBlock>(1);
|
||||
result->name_hint = name_hint;
|
||||
@ -48,10 +71,20 @@ static IrBasicBlock *ir_build_basic_block(IrBuilder *irb, const char *name_hint)
|
||||
return result;
|
||||
}
|
||||
|
||||
static IrBasicBlock *ir_build_bb_from(IrBuilder *irb, IrBasicBlock *other_bb) {
|
||||
IrBasicBlock *new_bb = ir_build_basic_block(irb, other_bb->name_hint);
|
||||
ir_link_new_bb(new_bb, other_bb);
|
||||
return new_bb;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionCondBr *) {
|
||||
return IrInstructionIdCondBr;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionBr *) {
|
||||
return IrInstructionIdBr;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchBr *) {
|
||||
return IrInstructionIdSwitchBr;
|
||||
}
|
||||
@ -96,8 +129,16 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCast *) {
|
||||
return IrInstructionIdCast;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionBr *) {
|
||||
return IrInstructionIdBr;
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionContainerInitList *) {
|
||||
return IrInstructionIdContainerInitList;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionContainerInitFields *) {
|
||||
return IrInstructionIdContainerInitFields;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnreachable *) {
|
||||
return IrInstructionIdUnreachable;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@ -123,6 +164,10 @@ static IrInstruction *ir_build_cast(IrBuilder *irb, AstNode *source_node, IrInst
|
||||
cast_instruction->dest_type = dest_type;
|
||||
cast_instruction->value = value;
|
||||
cast_instruction->cast_op = cast_op;
|
||||
|
||||
ir_ref_instruction(dest_type);
|
||||
ir_ref_instruction(value);
|
||||
|
||||
return &cast_instruction->base;
|
||||
}
|
||||
|
||||
@ -135,19 +180,44 @@ static IrInstruction *ir_build_cond_br(IrBuilder *irb, AstNode *source_node, IrI
|
||||
cond_br_instruction->condition = condition;
|
||||
cond_br_instruction->then_block = then_block;
|
||||
cond_br_instruction->else_block = else_block;
|
||||
|
||||
ir_ref_instruction(condition);
|
||||
ir_ref_bb(then_block);
|
||||
ir_ref_bb(else_block);
|
||||
|
||||
return &cond_br_instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_cond_br_from(IrBuilder *irb, IrInstruction *old_instruction,
|
||||
IrInstruction *condition, IrBasicBlock *then_block, IrBasicBlock *else_block)
|
||||
{
|
||||
IrInstruction *new_instruction = ir_build_cond_br(irb, old_instruction->source_node,
|
||||
condition, then_block, else_block);
|
||||
ir_link_new_instruction(new_instruction, old_instruction);
|
||||
return new_instruction;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_return(IrBuilder *irb, AstNode *source_node, IrInstruction *return_value) {
|
||||
IrInstructionReturn *return_instruction = ir_build_instruction<IrInstructionReturn>(irb, source_node);
|
||||
return_instruction->base.type_entry = irb->codegen->builtin_types.entry_unreachable;
|
||||
return_instruction->base.static_value.ok = true;
|
||||
return_instruction->value = return_value;
|
||||
|
||||
ir_ref_instruction(return_value);
|
||||
|
||||
return &return_instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_const(IrBuilder *irb, AstNode *source_node, TypeTableEntry *type_entry) {
|
||||
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, source_node);
|
||||
static IrInstruction *ir_build_return_from(IrBuilder *irb, IrInstruction *old_instruction,
|
||||
IrInstruction *return_value)
|
||||
{
|
||||
IrInstruction *new_instruction = ir_build_return(irb, old_instruction->source_node, return_value);
|
||||
ir_link_new_instruction(new_instruction, old_instruction);
|
||||
return new_instruction;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_create_const(IrBuilder *irb, AstNode *source_node, TypeTableEntry *type_entry) {
|
||||
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(irb->exec, source_node);
|
||||
const_instruction->base.type_entry = type_entry;
|
||||
const_instruction->base.static_value.ok = true;
|
||||
return &const_instruction->base;
|
||||
@ -206,9 +276,21 @@ static IrInstruction *ir_build_bin_op(IrBuilder *irb, AstNode *source_node, IrBi
|
||||
bin_op_instruction->op_id = op_id;
|
||||
bin_op_instruction->op1 = op1;
|
||||
bin_op_instruction->op2 = op2;
|
||||
|
||||
ir_ref_instruction(op1);
|
||||
ir_ref_instruction(op2);
|
||||
|
||||
return &bin_op_instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_bin_op_from(IrBuilder *irb, IrInstruction *old_instruction, IrBinOp op_id,
|
||||
IrInstruction *op1, IrInstruction *op2)
|
||||
{
|
||||
IrInstruction *new_instruction = ir_build_bin_op(irb, old_instruction->source_node, op_id, op1, op2);
|
||||
ir_link_new_instruction(new_instruction, old_instruction);
|
||||
return new_instruction;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_load_var(IrBuilder *irb, AstNode *source_node, VariableTableEntry *var) {
|
||||
IrInstructionLoadVar *load_var_instruction = ir_build_instruction<IrInstructionLoadVar>(irb, source_node);
|
||||
load_var_instruction->base.type_entry = var->type;
|
||||
@ -216,6 +298,15 @@ static IrInstruction *ir_build_load_var(IrBuilder *irb, AstNode *source_node, Va
|
||||
return &load_var_instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_load_var_from(IrBuilder *irb, IrInstruction *old_instruction,
|
||||
VariableTableEntry *var)
|
||||
{
|
||||
IrInstruction *new_instruction = ir_build_load_var(irb, old_instruction->source_node, var);
|
||||
ir_link_new_instruction(new_instruction, old_instruction);
|
||||
return new_instruction;
|
||||
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_call(IrBuilder *irb, AstNode *source_node,
|
||||
IrInstruction *fn, size_t arg_count, IrInstruction **args)
|
||||
{
|
||||
@ -223,6 +314,12 @@ static IrInstruction *ir_build_call(IrBuilder *irb, AstNode *source_node,
|
||||
call_instruction->fn = fn;
|
||||
call_instruction->arg_count = arg_count;
|
||||
call_instruction->args = args;
|
||||
|
||||
ir_ref_instruction(fn);
|
||||
for (size_t i = 0; i < arg_count; i += 1) {
|
||||
ir_ref_instruction(args[i]);
|
||||
}
|
||||
|
||||
return &call_instruction->base;
|
||||
}
|
||||
|
||||
@ -232,6 +329,11 @@ static IrInstruction *ir_build_builtin_call(IrBuilder *irb, AstNode *source_node
|
||||
IrInstructionBuiltinCall *call_instruction = ir_build_instruction<IrInstructionBuiltinCall>(irb, source_node);
|
||||
call_instruction->fn = fn;
|
||||
call_instruction->args = args;
|
||||
|
||||
for (size_t i = 0; i < fn->param_count; i += 1) {
|
||||
ir_ref_instruction(args[i]);
|
||||
}
|
||||
|
||||
return &call_instruction->base;
|
||||
}
|
||||
|
||||
@ -242,22 +344,105 @@ static IrInstruction *ir_build_phi(IrBuilder *irb, AstNode *source_node,
|
||||
phi_instruction->incoming_count = incoming_count;
|
||||
phi_instruction->incoming_blocks = incoming_blocks;
|
||||
phi_instruction->incoming_values = incoming_values;
|
||||
|
||||
for (size_t i = 0; i < incoming_count; i += 1) {
|
||||
ir_ref_instruction(incoming_values[i]);
|
||||
}
|
||||
|
||||
return &phi_instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_phi_from(IrBuilder *irb, IrInstruction *old_instruction,
|
||||
size_t incoming_count, IrBasicBlock **incoming_blocks, IrInstruction **incoming_values)
|
||||
{
|
||||
IrInstruction *new_instruction = ir_build_phi(irb, old_instruction->source_node,
|
||||
incoming_count, incoming_blocks, incoming_values);
|
||||
ir_link_new_instruction(new_instruction, old_instruction);
|
||||
return new_instruction;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_br(IrBuilder *irb, AstNode *source_node, IrBasicBlock *dest_block) {
|
||||
IrInstructionBr *br_instruction = ir_build_instruction<IrInstructionBr>(irb, source_node);
|
||||
br_instruction->dest_block = dest_block;
|
||||
|
||||
ir_ref_bb(dest_block);
|
||||
|
||||
return &br_instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_br_from(IrBuilder *irb, IrInstruction *old_instruction, IrBasicBlock *dest_block) {
|
||||
IrInstruction *new_instruction = ir_build_br(irb, old_instruction->source_node, dest_block);
|
||||
ir_link_new_instruction(new_instruction, old_instruction);
|
||||
return new_instruction;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_un_op(IrBuilder *irb, AstNode *source_node, IrUnOp op_id, IrInstruction *value) {
|
||||
IrInstructionUnOp *br_instruction = ir_build_instruction<IrInstructionUnOp>(irb, source_node);
|
||||
br_instruction->op_id = op_id;
|
||||
br_instruction->value = value;
|
||||
|
||||
ir_ref_instruction(value);
|
||||
|
||||
return &br_instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_un_op_from(IrBuilder *irb, IrInstruction *old_instruction,
|
||||
IrUnOp op_id, IrInstruction *value)
|
||||
{
|
||||
IrInstruction *new_instruction = ir_build_un_op(irb, old_instruction->source_node, op_id, value);
|
||||
ir_link_new_instruction(new_instruction, old_instruction);
|
||||
return new_instruction;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_container_init_list(IrBuilder *irb, AstNode *source_node,
|
||||
IrInstruction *container_type, size_t item_count, IrInstruction **items)
|
||||
{
|
||||
IrInstructionContainerInitList *container_init_list_instruction =
|
||||
ir_build_instruction<IrInstructionContainerInitList>(irb, source_node);
|
||||
container_init_list_instruction->container_type = container_type;
|
||||
container_init_list_instruction->item_count = item_count;
|
||||
container_init_list_instruction->items = items;
|
||||
|
||||
ir_ref_instruction(container_type);
|
||||
for (size_t i = 0; i < item_count; i += 1) {
|
||||
ir_ref_instruction(items[i]);
|
||||
}
|
||||
|
||||
return &container_init_list_instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_container_init_fields(IrBuilder *irb, AstNode *source_node,
|
||||
IrInstruction *container_type, size_t field_count, Buf **field_names, IrInstruction **field_values)
|
||||
{
|
||||
IrInstructionContainerInitFields *container_init_fields_instruction =
|
||||
ir_build_instruction<IrInstructionContainerInitFields>(irb, source_node);
|
||||
container_init_fields_instruction->container_type = container_type;
|
||||
container_init_fields_instruction->field_count = field_count;
|
||||
container_init_fields_instruction->field_names = field_names;
|
||||
container_init_fields_instruction->field_values = field_values;
|
||||
|
||||
ir_ref_instruction(container_type);
|
||||
for (size_t i = 0; i < field_count; i += 1) {
|
||||
ir_ref_instruction(field_values[i]);
|
||||
}
|
||||
|
||||
return &container_init_fields_instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_unreachable(IrBuilder *irb, AstNode *source_node) {
|
||||
IrInstructionUnreachable *unreachable_instruction =
|
||||
ir_build_instruction<IrInstructionUnreachable>(irb, source_node);
|
||||
unreachable_instruction->base.static_value.ok = true;
|
||||
unreachable_instruction->base.type_entry = irb->codegen->builtin_types.entry_unreachable;
|
||||
return &unreachable_instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_unreachable_from(IrBuilder *irb, IrInstruction *old_instruction) {
|
||||
IrInstruction *new_instruction = ir_build_unreachable(irb, old_instruction->source_node);
|
||||
ir_link_new_instruction(new_instruction, old_instruction);
|
||||
return new_instruction;
|
||||
}
|
||||
|
||||
|
||||
//static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) {
|
||||
// size_t result = 0;
|
||||
@ -524,6 +709,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
|
||||
|
||||
builtin_fn->ref_count += 1;
|
||||
|
||||
if (builtin_fn->id == BuiltinFnIdUnreachable) {
|
||||
return ir_build_unreachable(irb, node);
|
||||
}
|
||||
|
||||
IrInstruction **args = allocate<IrInstruction *>(actual_param_count);
|
||||
for (size_t i = 0; i < actual_param_count; i += 1) {
|
||||
AstNode *arg_node = node->data.fn_call_expr.params.at(i);
|
||||
@ -649,6 +838,51 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, AstNode *node) {
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, AstNode *node) {
|
||||
assert(node->type == NodeTypeContainerInitExpr);
|
||||
|
||||
AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr;
|
||||
ContainerInitKind kind = container_init_expr->kind;
|
||||
|
||||
IrInstruction *container_type = ir_gen_node(irb, container_init_expr->type, node->block_context);
|
||||
if (container_type == irb->codegen->invalid_instruction)
|
||||
return container_type;
|
||||
|
||||
if (kind == ContainerInitKindStruct) {
|
||||
size_t field_count = container_init_expr->entries.length;
|
||||
IrInstruction **values = allocate<IrInstruction *>(field_count);
|
||||
Buf **names = allocate<Buf *>(field_count);
|
||||
for (size_t i = 0; i < field_count; i += 1) {
|
||||
AstNode *entry_node = container_init_expr->entries.at(i);
|
||||
assert(entry_node->type == NodeTypeStructValueField);
|
||||
|
||||
Buf *name = entry_node->data.struct_val_field.name;
|
||||
AstNode *expr_node = entry_node->data.struct_val_field.expr;
|
||||
IrInstruction *expr_value = ir_gen_node(irb, expr_node, node->block_context);
|
||||
if (expr_value == irb->codegen->invalid_instruction)
|
||||
return expr_value;
|
||||
|
||||
names[i] = name;
|
||||
values[i] = expr_value;
|
||||
}
|
||||
return ir_build_container_init_fields(irb, node, container_type, field_count, names, values);
|
||||
} else if (kind == ContainerInitKindArray) {
|
||||
size_t item_count = container_init_expr->entries.length;
|
||||
IrInstruction **values = allocate<IrInstruction *>(item_count);
|
||||
for (size_t i = 0; i < item_count; i += 1) {
|
||||
AstNode *expr_node = container_init_expr->entries.at(i);
|
||||
IrInstruction *expr_value = ir_gen_node(irb, expr_node, node->block_context);
|
||||
if (expr_value == irb->codegen->invalid_instruction)
|
||||
return expr_value;
|
||||
|
||||
values[i] = expr_value;
|
||||
}
|
||||
return ir_build_container_init_list(irb, node, container_type, item_count, values);
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockContext *block_context,
|
||||
bool pointer_only)
|
||||
{
|
||||
@ -670,6 +904,8 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockCont
|
||||
return ir_gen_if_bool_expr(irb, node);
|
||||
case NodeTypePrefixOpExpr:
|
||||
return ir_gen_prefix_op_expr(irb, node);
|
||||
case NodeTypeContainerInitExpr:
|
||||
return ir_gen_container_init_expr(irb, node);
|
||||
case NodeTypeUnwrapErrorExpr:
|
||||
case NodeTypeReturnExpr:
|
||||
case NodeTypeDefer:
|
||||
@ -685,7 +921,6 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockCont
|
||||
case NodeTypeBreak:
|
||||
case NodeTypeContinue:
|
||||
case NodeTypeLabel:
|
||||
case NodeTypeContainerInitExpr:
|
||||
case NodeTypeSwitchExpr:
|
||||
case NodeTypeBoolLiteral:
|
||||
case NodeTypeStringLiteral:
|
||||
@ -733,6 +968,8 @@ static IrInstruction *ir_gen_add_return(CodeGen *g, AstNode *node, BlockContext
|
||||
irb->exec = ir_executable;
|
||||
|
||||
irb->current_basic_block = ir_build_basic_block(irb, "Entry");
|
||||
// Entry block gets a reference because we enter it to begin.
|
||||
ir_ref_bb(irb->current_basic_block);
|
||||
|
||||
IrInstruction *result = ir_gen_node_extra(irb, node, scope, pointer_only);
|
||||
assert(result);
|
||||
@ -767,11 +1004,6 @@ IrInstruction *ir_gen_fn(CodeGen *codegn, FnTableEntry *fn_entry) {
|
||||
return ir_gen_add_return(codegn, body_node, scope, ir_executable, add_return_yes, pointer_only_no);
|
||||
}
|
||||
|
||||
static void ir_link_new(IrInstruction *new_instruction, IrInstruction *old_instruction) {
|
||||
new_instruction->other = old_instruction;
|
||||
old_instruction->other = new_instruction;
|
||||
}
|
||||
|
||||
/*
|
||||
static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *node) {
|
||||
assert(node->type == NodeTypeGoto);
|
||||
@ -1033,7 +1265,7 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst
|
||||
TypeTableEntry *wanted_type = dest_type->static_value.data.x_type;
|
||||
|
||||
if (value->static_value.ok) {
|
||||
IrInstruction *result = ir_build_const(&ira->new_irb, source_instr->source_node, wanted_type);
|
||||
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->source_node, wanted_type);
|
||||
eval_const_expr_implicit_cast(cast_op, &value->static_value, value->type_entry,
|
||||
&result->static_value, wanted_type);
|
||||
return result;
|
||||
@ -1058,6 +1290,12 @@ static bool is_u8(TypeTableEntry *type) {
|
||||
!type->data.integral.is_signed && type->data.integral.bit_count == 8;
|
||||
}
|
||||
|
||||
static IrBasicBlock *ir_get_new_bb(IrAnalyze *ira, IrBasicBlock *old_bb) {
|
||||
if (old_bb->other)
|
||||
return old_bb->other;
|
||||
return ir_build_bb_from(&ira->new_irb, old_bb);
|
||||
}
|
||||
|
||||
static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
|
||||
IrInstruction *dest_type, IrInstruction *value)
|
||||
{
|
||||
@ -1365,10 +1603,7 @@ static TypeTableEntry *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructi
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
ir_link_new(ir_build_return(&ira->new_irb, return_instruction->base.source_node, value),
|
||||
&return_instruction->base);
|
||||
|
||||
return ira->codegen->builtin_types.entry_unreachable;
|
||||
return ir_build_return_from(&ira->new_irb, &return_instruction->base, value)->type_entry;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_const(IrAnalyze *ira, IrInstructionConst *const_instruction) {
|
||||
@ -1411,8 +1646,7 @@ static TypeTableEntry *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp
|
||||
return bool_type;
|
||||
}
|
||||
|
||||
ir_link_new(ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.source_node,
|
||||
bin_op_instruction->op_id, op1->other, op2->other), &bin_op_instruction->base);
|
||||
ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, bin_op_instruction->op_id, op1->other, op2->other);
|
||||
|
||||
return bool_type;
|
||||
}
|
||||
@ -1481,8 +1715,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
|
||||
|
||||
zig_panic("TODO interpret bin_op_cmp");
|
||||
|
||||
ir_link_new(ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.source_node,
|
||||
op_id, op1->other, op2->other), &bin_op_instruction->base);
|
||||
ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id, op1->other, op2->other);
|
||||
|
||||
return ira->codegen->builtin_types.entry_bool;
|
||||
}
|
||||
@ -1639,8 +1872,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
|
||||
|
||||
}
|
||||
|
||||
ir_link_new(ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.source_node,
|
||||
op_id, op1->other, op2->other), &bin_op_instruction->base);
|
||||
ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id, op1->other, op2->other);
|
||||
|
||||
return resolved_type;
|
||||
}
|
||||
@ -1684,8 +1916,7 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_load_var(IrAnalyze *ira, IrInstructionLoadVar *load_var_instruction) {
|
||||
ir_link_new(ir_build_load_var(&ira->new_irb, load_var_instruction->base.source_node,
|
||||
load_var_instruction->var), &load_var_instruction->base);
|
||||
ir_build_load_var_from(&ira->new_irb, &load_var_instruction->base, load_var_instruction->var);
|
||||
return load_var_instruction->var->type;
|
||||
}
|
||||
|
||||
@ -1709,15 +1940,14 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
|
||||
if (cast_instruction == ira->codegen->invalid_instruction)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
ir_link_new(cast_instruction, &call_instruction->base);
|
||||
ir_link_new_instruction(cast_instruction, &call_instruction->base);
|
||||
return cast_instruction->type_entry;
|
||||
} else {
|
||||
zig_panic("TODO analyze more fn call types");
|
||||
}
|
||||
} else {
|
||||
//ir_link_new(ir_build_call(&ira->new_irb, call_instruction->base.source_node,
|
||||
// call_instruction->fn, call_instruction->arg_count, call_instruction->args),
|
||||
// &call_instruction->base);
|
||||
//ir_build_call_from(&ira->new_irb, &call_instruction->base,
|
||||
// call_instruction->fn, call_instruction->arg_count, call_instruction->args);
|
||||
|
||||
zig_panic("TODO analyze fn call");
|
||||
}
|
||||
@ -1726,7 +1956,7 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
|
||||
static TypeTableEntry *ir_analyze_unary_bool_not(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
|
||||
TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool;
|
||||
|
||||
IrInstruction *casted_value = ir_get_casted_value(ira, un_op_instruction->value, bool_type);
|
||||
IrInstruction *casted_value = ir_get_casted_value(ira, un_op_instruction->value->other, bool_type);
|
||||
if (casted_value == ira->codegen->invalid_instruction)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
@ -1739,9 +1969,7 @@ static TypeTableEntry *ir_analyze_unary_bool_not(IrAnalyze *ira, IrInstructionUn
|
||||
return bool_type;
|
||||
}
|
||||
|
||||
IrInstruction *new_instruction = ir_build_un_op(&ira->new_irb, un_op_instruction->base.source_node,
|
||||
IrUnOpBoolNot, casted_value);
|
||||
ir_link_new(new_instruction, &un_op_instruction->base);
|
||||
ir_build_un_op_from(&ira->new_irb, &un_op_instruction->base, IrUnOpBoolNot, casted_value);
|
||||
|
||||
return bool_type;
|
||||
}
|
||||
@ -2924,8 +3152,6 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio
|
||||
// return analyze_compile_err(g, import, context, node);
|
||||
// case BuiltinFnIdIntType:
|
||||
// return analyze_int_type(g, import, context, node);
|
||||
// case BuiltinFnIdUnreachable:
|
||||
// return g->builtin_types.entry_unreachable;
|
||||
// case BuiltinFnIdSetFnTest:
|
||||
// return analyze_set_fn_test(g, import, context, node);
|
||||
// case BuiltinFnIdSetFnNoInline:
|
||||
@ -2940,6 +3166,300 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio
|
||||
// zig_unreachable();
|
||||
//}
|
||||
|
||||
//static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry *import,
|
||||
// BlockContext *context, AstNode *node)
|
||||
//{
|
||||
// assert(node->type == NodeTypeContainerInitExpr);
|
||||
//
|
||||
// AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr;
|
||||
//
|
||||
// ContainerInitKind kind = container_init_expr->kind;
|
||||
//
|
||||
// if (container_init_expr->type->type == NodeTypeFieldAccessExpr) {
|
||||
// container_init_expr->type->data.field_access_expr.container_init_expr_node = node;
|
||||
// }
|
||||
//
|
||||
// TypeTableEntry *container_meta_type = analyze_expression(g, import, context, nullptr,
|
||||
// container_init_expr->type);
|
||||
//
|
||||
// if (container_meta_type->id == TypeTableEntryIdInvalid) {
|
||||
// return g->builtin_types.entry_invalid;
|
||||
// }
|
||||
//
|
||||
// if (node->data.container_init_expr.enum_type) {
|
||||
// get_resolved_expr(node)->const_val = get_resolved_expr(container_init_expr->type)->const_val;
|
||||
// return node->data.container_init_expr.enum_type;
|
||||
// }
|
||||
//
|
||||
// TypeTableEntry *container_type = resolve_type(g, container_init_expr->type);
|
||||
//
|
||||
// if (container_type->id == TypeTableEntryIdInvalid) {
|
||||
// return container_type;
|
||||
// } else if (container_type->id == TypeTableEntryIdStruct &&
|
||||
// !container_type->data.structure.is_slice &&
|
||||
// (kind == ContainerInitKindStruct || (kind == ContainerInitKindArray &&
|
||||
// container_init_expr->entries.length == 0)))
|
||||
// {
|
||||
// StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr;
|
||||
// codegen->type_entry = container_type;
|
||||
// codegen->source_node = node;
|
||||
//
|
||||
//
|
||||
// size_t expr_field_count = container_init_expr->entries.length;
|
||||
// size_t actual_field_count = container_type->data.structure.src_field_count;
|
||||
//
|
||||
// AstNode *non_const_expr_culprit = nullptr;
|
||||
//
|
||||
// size_t *field_use_counts = allocate<size_t>(actual_field_count);
|
||||
// ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
|
||||
// const_val->ok = true;
|
||||
// const_val->data.x_struct.fields = allocate<ConstExprValue*>(actual_field_count);
|
||||
// for (size_t i = 0; i < expr_field_count; i += 1) {
|
||||
// AstNode *val_field_node = container_init_expr->entries.at(i);
|
||||
// assert(val_field_node->type == NodeTypeStructValueField);
|
||||
//
|
||||
// val_field_node->block_context = context;
|
||||
//
|
||||
// TypeStructField *type_field = find_struct_type_field(container_type,
|
||||
// val_field_node->data.struct_val_field.name);
|
||||
//
|
||||
// if (!type_field) {
|
||||
// add_node_error(g, val_field_node,
|
||||
// buf_sprintf("no member named '%s' in '%s'",
|
||||
// buf_ptr(val_field_node->data.struct_val_field.name), buf_ptr(&container_type->name)));
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// if (type_field->type_entry->id == TypeTableEntryIdInvalid) {
|
||||
// return g->builtin_types.entry_invalid;
|
||||
// }
|
||||
//
|
||||
// size_t field_index = type_field->src_index;
|
||||
// field_use_counts[field_index] += 1;
|
||||
// if (field_use_counts[field_index] > 1) {
|
||||
// add_node_error(g, val_field_node, buf_sprintf("duplicate field"));
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// val_field_node->data.struct_val_field.type_struct_field = type_field;
|
||||
//
|
||||
// analyze_expression(g, import, context, type_field->type_entry,
|
||||
// val_field_node->data.struct_val_field.expr);
|
||||
//
|
||||
// if (const_val->ok) {
|
||||
// ConstExprValue *field_val =
|
||||
// &get_resolved_expr(val_field_node->data.struct_val_field.expr)->const_val;
|
||||
// if (field_val->ok) {
|
||||
// const_val->data.x_struct.fields[field_index] = field_val;
|
||||
// const_val->depends_on_compile_var = const_val->depends_on_compile_var || field_val->depends_on_compile_var;
|
||||
// } else {
|
||||
// const_val->ok = false;
|
||||
// non_const_expr_culprit = val_field_node->data.struct_val_field.expr;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if (!const_val->ok) {
|
||||
// assert(non_const_expr_culprit);
|
||||
// if (context->fn_entry) {
|
||||
// context->fn_entry->struct_val_expr_alloca_list.append(codegen);
|
||||
// } else {
|
||||
// add_node_error(g, non_const_expr_culprit, buf_sprintf("unable to evaluate constant expression"));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// for (size_t i = 0; i < actual_field_count; i += 1) {
|
||||
// if (field_use_counts[i] == 0) {
|
||||
// add_node_error(g, node,
|
||||
// buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i].name)));
|
||||
// }
|
||||
// }
|
||||
// return container_type;
|
||||
// } else if (container_type->id == TypeTableEntryIdStruct &&
|
||||
// container_type->data.structure.is_slice &&
|
||||
// kind == ContainerInitKindArray)
|
||||
// {
|
||||
// size_t elem_count = container_init_expr->entries.length;
|
||||
//
|
||||
// TypeTableEntry *pointer_type = container_type->data.structure.fields[0].type_entry;
|
||||
// assert(pointer_type->id == TypeTableEntryIdPointer);
|
||||
// TypeTableEntry *child_type = pointer_type->data.pointer.child_type;
|
||||
//
|
||||
// ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
|
||||
// const_val->ok = true;
|
||||
// const_val->data.x_array.fields = allocate<ConstExprValue*>(elem_count);
|
||||
//
|
||||
// for (size_t i = 0; i < elem_count; i += 1) {
|
||||
// AstNode **elem_node = &container_init_expr->entries.at(i);
|
||||
// analyze_expression(g, import, context, child_type, *elem_node);
|
||||
//
|
||||
// if (const_val->ok) {
|
||||
// ConstExprValue *elem_const_val = &get_resolved_expr(*elem_node)->const_val;
|
||||
// if (elem_const_val->ok) {
|
||||
// const_val->data.x_array.fields[i] = elem_const_val;
|
||||
// const_val->depends_on_compile_var = const_val->depends_on_compile_var ||
|
||||
// elem_const_val->depends_on_compile_var;
|
||||
// } else {
|
||||
// const_val->ok = false;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// TypeTableEntry *fixed_size_array_type = get_array_type(g, child_type, elem_count);
|
||||
//
|
||||
// StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr;
|
||||
// codegen->type_entry = fixed_size_array_type;
|
||||
// codegen->source_node = node;
|
||||
// if (!const_val->ok) {
|
||||
// if (!context->fn_entry) {
|
||||
// add_node_error(g, node,
|
||||
// buf_sprintf("unable to evaluate constant expression"));
|
||||
// } else {
|
||||
// context->fn_entry->struct_val_expr_alloca_list.append(codegen);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return fixed_size_array_type;
|
||||
// } else if (container_type->id == TypeTableEntryIdArray) {
|
||||
// zig_panic("TODO array container init");
|
||||
// return container_type;
|
||||
// } else if (container_type->id == TypeTableEntryIdVoid) {
|
||||
// if (container_init_expr->entries.length != 0) {
|
||||
// add_node_error(g, node, buf_sprintf("void expression expects no arguments"));
|
||||
// return g->builtin_types.entry_invalid;
|
||||
// } else {
|
||||
// return resolve_expr_const_val_as_void(g, node);
|
||||
// }
|
||||
// } else {
|
||||
// add_node_error(g, node,
|
||||
// buf_sprintf("type '%s' does not support %s initialization syntax",
|
||||
// buf_ptr(&container_type->name), err_container_init_syntax_name(kind)));
|
||||
// return g->builtin_types.entry_invalid;
|
||||
// }
|
||||
//}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_br(IrAnalyze *ira, IrInstructionBr *br_instruction) {
|
||||
IrBasicBlock *old_dest_block = br_instruction->dest_block;
|
||||
IrBasicBlock *new_bb = ir_get_new_bb(ira, old_dest_block);
|
||||
ir_build_br_from(&ira->new_irb, &br_instruction->base, new_bb);
|
||||
return ira->codegen->builtin_types.entry_unreachable;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructionCondBr *cond_br_instruction) {
|
||||
TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool;
|
||||
IrInstruction *condition = ir_get_casted_value(ira, cond_br_instruction->condition->other, bool_type);
|
||||
if (condition == ira->codegen->invalid_instruction)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
if (condition->static_value.ok) {
|
||||
IrBasicBlock *old_dest_block;
|
||||
IrBasicBlock *old_ignored_block;
|
||||
if (condition->static_value.data.x_bool) {
|
||||
old_dest_block = cond_br_instruction->then_block;
|
||||
old_ignored_block = cond_br_instruction->else_block;
|
||||
} else {
|
||||
old_dest_block = cond_br_instruction->else_block;
|
||||
old_ignored_block = cond_br_instruction->then_block;
|
||||
}
|
||||
ir_unref_bb(old_ignored_block);
|
||||
IrBasicBlock *new_bb = ir_get_new_bb(ira, old_dest_block);
|
||||
ir_build_br_from(&ira->new_irb, &cond_br_instruction->base, new_bb);
|
||||
return ira->codegen->builtin_types.entry_unreachable;
|
||||
}
|
||||
|
||||
IrBasicBlock *new_then_block = ir_get_new_bb(ira, cond_br_instruction->then_block);
|
||||
IrBasicBlock *new_else_block = ir_get_new_bb(ira, cond_br_instruction->else_block);
|
||||
ir_build_cond_br_from(&ira->new_irb, &cond_br_instruction->base, condition, new_then_block, new_else_block);
|
||||
return ira->codegen->builtin_types.entry_unreachable;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_builtin_call(IrAnalyze *ira,
|
||||
IrInstructionBuiltinCall *builtin_call_instruction)
|
||||
{
|
||||
switch (builtin_call_instruction->fn->id) {
|
||||
case BuiltinFnIdInvalid:
|
||||
case BuiltinFnIdUnreachable:
|
||||
zig_unreachable();
|
||||
case BuiltinFnIdMemcpy:
|
||||
case BuiltinFnIdMemset:
|
||||
case BuiltinFnIdSizeof:
|
||||
case BuiltinFnIdAlignof:
|
||||
case BuiltinFnIdMaxValue:
|
||||
case BuiltinFnIdMinValue:
|
||||
case BuiltinFnIdMemberCount:
|
||||
case BuiltinFnIdTypeof:
|
||||
case BuiltinFnIdAddWithOverflow:
|
||||
case BuiltinFnIdSubWithOverflow:
|
||||
case BuiltinFnIdMulWithOverflow:
|
||||
case BuiltinFnIdShlWithOverflow:
|
||||
case BuiltinFnIdCInclude:
|
||||
case BuiltinFnIdCDefine:
|
||||
case BuiltinFnIdCUndef:
|
||||
case BuiltinFnIdCompileVar:
|
||||
case BuiltinFnIdCompileErr:
|
||||
case BuiltinFnIdConstEval:
|
||||
case BuiltinFnIdCtz:
|
||||
case BuiltinFnIdClz:
|
||||
case BuiltinFnIdImport:
|
||||
case BuiltinFnIdCImport:
|
||||
case BuiltinFnIdErrName:
|
||||
case BuiltinFnIdBreakpoint:
|
||||
case BuiltinFnIdReturnAddress:
|
||||
case BuiltinFnIdFrameAddress:
|
||||
case BuiltinFnIdEmbedFile:
|
||||
case BuiltinFnIdCmpExchange:
|
||||
case BuiltinFnIdFence:
|
||||
case BuiltinFnIdDivExact:
|
||||
case BuiltinFnIdTruncate:
|
||||
case BuiltinFnIdIntType:
|
||||
case BuiltinFnIdSetFnTest:
|
||||
case BuiltinFnIdSetFnVisible:
|
||||
case BuiltinFnIdSetFnStaticEval:
|
||||
case BuiltinFnIdSetFnNoInline:
|
||||
case BuiltinFnIdSetDebugSafety:
|
||||
zig_panic("TODO analyze more builtin functions");
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_unreachable(IrAnalyze *ira,
|
||||
IrInstructionUnreachable *unreachable_instruction)
|
||||
{
|
||||
return ir_build_unreachable_from(&ira->new_irb, &unreachable_instruction->base)->type_entry;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPhi *phi_instruction) {
|
||||
ZigList<IrBasicBlock*> new_incoming_blocks = {0};
|
||||
ZigList<IrInstruction*> new_incoming_values = {0};
|
||||
|
||||
for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) {
|
||||
IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i];
|
||||
if (predecessor->ref_count == 0)
|
||||
continue;
|
||||
|
||||
assert(predecessor->other);
|
||||
new_incoming_blocks.append(predecessor->other);
|
||||
|
||||
IrInstruction *old_value = phi_instruction->incoming_values[i];
|
||||
assert(old_value);
|
||||
new_incoming_values.append(old_value->other);
|
||||
}
|
||||
assert(new_incoming_blocks.length != 0);
|
||||
|
||||
if (new_incoming_blocks.length == 1) {
|
||||
IrInstruction *first_value = new_incoming_values.at(0);
|
||||
phi_instruction->base.other = first_value;
|
||||
return first_value->type_entry;
|
||||
}
|
||||
|
||||
TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, &phi_instruction->base,
|
||||
new_incoming_values.items, new_incoming_values.length);
|
||||
if (resolved_type->id == TypeTableEntryIdInvalid)
|
||||
return resolved_type;
|
||||
|
||||
ir_build_phi_from(&ira->new_irb, &phi_instruction->base, new_incoming_blocks.length,
|
||||
new_incoming_blocks.items, new_incoming_values.items);
|
||||
return resolved_type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
|
||||
switch (instruction->id) {
|
||||
@ -2957,13 +3477,21 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
||||
return ir_analyze_instruction_load_var(ira, (IrInstructionLoadVar *)instruction);
|
||||
case IrInstructionIdCall:
|
||||
return ir_analyze_instruction_call(ira, (IrInstructionCall *)instruction);
|
||||
case IrInstructionIdCondBr:
|
||||
case IrInstructionIdBr:
|
||||
case IrInstructionIdSwitchBr:
|
||||
case IrInstructionIdPhi:
|
||||
case IrInstructionIdStoreVar:
|
||||
return ir_analyze_instruction_br(ira, (IrInstructionBr *)instruction);
|
||||
case IrInstructionIdCondBr:
|
||||
return ir_analyze_instruction_cond_br(ira, (IrInstructionCondBr *)instruction);
|
||||
case IrInstructionIdBuiltinCall:
|
||||
return ir_analyze_instruction_builtin_call(ira, (IrInstructionBuiltinCall *)instruction);
|
||||
case IrInstructionIdUnreachable:
|
||||
return ir_analyze_instruction_unreachable(ira, (IrInstructionUnreachable *)instruction);
|
||||
case IrInstructionIdPhi:
|
||||
return ir_analyze_instruction_phi(ira, (IrInstructionPhi *)instruction);
|
||||
case IrInstructionIdSwitchBr:
|
||||
case IrInstructionIdStoreVar:
|
||||
case IrInstructionIdCast:
|
||||
case IrInstructionIdContainerInitList:
|
||||
case IrInstructionIdContainerInitFields:
|
||||
zig_panic("TODO analyze more instructions");
|
||||
}
|
||||
zig_unreachable();
|
||||
@ -3000,25 +3528,106 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl
|
||||
ira->exec_context.var_slot_list = allocate<IrVarSlot>(ira->exec_context.var_slot_count);
|
||||
|
||||
TypeTableEntry *return_type = ira->codegen->builtin_types.entry_void;
|
||||
for (size_t bb_i = 0; bb_i < ira->old_irb.exec->basic_block_list.length; bb_i += 1) {
|
||||
ira->old_irb.current_basic_block = ira->old_irb.exec->basic_block_list.at(bb_i);
|
||||
if (ira->old_irb.current_basic_block->ref_count == 0)
|
||||
continue;
|
||||
|
||||
ira->new_irb.current_basic_block = ir_build_basic_block(&ira->new_irb, "Entry");
|
||||
ira->old_irb.current_basic_block = ira->old_irb.exec->basic_block_list.at(0);
|
||||
ira->new_irb.current_basic_block = ir_get_new_bb(ira, ira->old_irb.current_basic_block);
|
||||
|
||||
ira->new_irb.current_basic_block->other = ira->old_irb.current_basic_block;
|
||||
ira->old_irb.current_basic_block->other = ira->new_irb.current_basic_block;
|
||||
return_type = ira->codegen->builtin_types.entry_void;
|
||||
|
||||
|
||||
for (size_t i = 0; i < ira->old_irb.current_basic_block->instruction_list.length; i += 1) {
|
||||
IrInstruction *instruction = ira->old_irb.current_basic_block->instruction_list.at(i);
|
||||
if (return_type->id == TypeTableEntryIdUnreachable) {
|
||||
add_node_error(ira->codegen, first_executing_node(instruction->source_node),
|
||||
buf_sprintf("unreachable code"));
|
||||
break;
|
||||
for (size_t instr_i = 0; instr_i < ira->old_irb.current_basic_block->instruction_list.length; instr_i += 1) {
|
||||
IrInstruction *instruction = ira->old_irb.current_basic_block->instruction_list.at(instr_i);
|
||||
if (return_type->id == TypeTableEntryIdUnreachable) {
|
||||
// TODO
|
||||
//add_node_error(ira->codegen, first_executing_node(instruction->source_node),
|
||||
// buf_sprintf("unreachable code"));
|
||||
break;
|
||||
}
|
||||
bool is_last = (instr_i == ira->old_irb.current_basic_block->instruction_list.length - 1);
|
||||
TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr;
|
||||
return_type = ir_analyze_instruction(ira, instruction, passed_expected_type);
|
||||
}
|
||||
bool is_last = (i == ira->old_irb.current_basic_block->instruction_list.length - 1);
|
||||
TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr;
|
||||
return_type = ir_analyze_instruction(ira, instruction, passed_expected_type);
|
||||
}
|
||||
|
||||
// Give entry block a ref
|
||||
ir_ref_bb(ira->new_irb.exec->basic_block_list.at(0));
|
||||
|
||||
return return_type;
|
||||
}
|
||||
|
||||
static bool ir_builtin_call_has_side_effects(IrInstructionBuiltinCall *call_instruction) {
|
||||
switch (call_instruction->fn->id) {
|
||||
case BuiltinFnIdInvalid:
|
||||
zig_unreachable();
|
||||
case BuiltinFnIdMemcpy:
|
||||
case BuiltinFnIdMemset:
|
||||
case BuiltinFnIdAddWithOverflow:
|
||||
case BuiltinFnIdSubWithOverflow:
|
||||
case BuiltinFnIdMulWithOverflow:
|
||||
case BuiltinFnIdShlWithOverflow:
|
||||
case BuiltinFnIdCInclude:
|
||||
case BuiltinFnIdCDefine:
|
||||
case BuiltinFnIdCUndef:
|
||||
case BuiltinFnIdImport:
|
||||
case BuiltinFnIdCImport:
|
||||
case BuiltinFnIdBreakpoint:
|
||||
case BuiltinFnIdEmbedFile:
|
||||
case BuiltinFnIdCmpExchange:
|
||||
case BuiltinFnIdFence:
|
||||
case BuiltinFnIdUnreachable:
|
||||
case BuiltinFnIdSetFnTest:
|
||||
case BuiltinFnIdSetFnVisible:
|
||||
case BuiltinFnIdSetFnStaticEval:
|
||||
case BuiltinFnIdSetFnNoInline:
|
||||
case BuiltinFnIdSetDebugSafety:
|
||||
return true;
|
||||
case BuiltinFnIdSizeof:
|
||||
case BuiltinFnIdAlignof:
|
||||
case BuiltinFnIdMaxValue:
|
||||
case BuiltinFnIdMinValue:
|
||||
case BuiltinFnIdMemberCount:
|
||||
case BuiltinFnIdTypeof:
|
||||
case BuiltinFnIdCompileVar:
|
||||
case BuiltinFnIdCompileErr:
|
||||
case BuiltinFnIdConstEval:
|
||||
case BuiltinFnIdCtz:
|
||||
case BuiltinFnIdClz:
|
||||
case BuiltinFnIdErrName:
|
||||
case BuiltinFnIdReturnAddress:
|
||||
case BuiltinFnIdFrameAddress:
|
||||
case BuiltinFnIdDivExact:
|
||||
case BuiltinFnIdTruncate:
|
||||
case BuiltinFnIdIntType:
|
||||
return false;
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
switch (instruction->id) {
|
||||
case IrInstructionIdInvalid:
|
||||
zig_unreachable();
|
||||
case IrInstructionIdBr:
|
||||
case IrInstructionIdCondBr:
|
||||
case IrInstructionIdSwitchBr:
|
||||
case IrInstructionIdStoreVar:
|
||||
case IrInstructionIdCall:
|
||||
case IrInstructionIdReturn:
|
||||
case IrInstructionIdUnreachable:
|
||||
return true;
|
||||
case IrInstructionIdPhi:
|
||||
case IrInstructionIdUnOp:
|
||||
case IrInstructionIdBinOp:
|
||||
case IrInstructionIdLoadVar:
|
||||
case IrInstructionIdConst:
|
||||
case IrInstructionIdCast:
|
||||
case IrInstructionIdContainerInitList:
|
||||
case IrInstructionIdContainerInitFields:
|
||||
return false;
|
||||
case IrInstructionIdBuiltinCall:
|
||||
return ir_builtin_call_has_side_effects((IrInstructionBuiltinCall *)instruction);
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
@ -16,4 +16,6 @@ IrInstruction *ir_gen_fn(CodeGen *g, FnTableEntry *fn_entry);
|
||||
TypeTableEntry *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_executable,
|
||||
TypeTableEntry *expected_type);
|
||||
|
||||
bool ir_has_side_effects(IrInstruction *instruction);
|
||||
|
||||
#endif
|
||||
|
160
src/ir_print.cpp
160
src/ir_print.cpp
@ -1,3 +1,4 @@
|
||||
#include "ir.hpp"
|
||||
#include "ir_print.hpp"
|
||||
|
||||
struct IrPrint {
|
||||
@ -14,49 +15,46 @@ static void ir_print_indent(IrPrint *irp) {
|
||||
|
||||
static void ir_print_prefix(IrPrint *irp, IrInstruction *instruction) {
|
||||
ir_print_indent(irp);
|
||||
fprintf(irp->f, "#%-3zu| ", instruction->debug_id);
|
||||
const char *type_name = instruction->type_entry ? buf_ptr(&instruction->type_entry->name) : "(unknown)";
|
||||
const char *ref_count = ir_has_side_effects(instruction) ?
|
||||
"-" : buf_ptr(buf_sprintf("%zu", instruction->ref_count));
|
||||
fprintf(irp->f, "#%-3zu| %-12s| %-2s| ", instruction->debug_id, type_name, ref_count);
|
||||
}
|
||||
|
||||
static void ir_print_return(IrPrint *irp, IrInstructionReturn *return_instruction) {
|
||||
ir_print_prefix(irp, &return_instruction->base);
|
||||
assert(return_instruction->value);
|
||||
fprintf(irp->f, "return #%zu\n", return_instruction->value->debug_id);
|
||||
}
|
||||
|
||||
static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction) {
|
||||
ir_print_prefix(irp, &const_instruction->base);
|
||||
TypeTableEntry *type_entry = const_instruction->base.type_entry;
|
||||
fprintf(irp->f, "%s ", buf_ptr(&type_entry->name));
|
||||
static void ir_print_const_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
TypeTableEntry *type_entry = instruction->type_entry;
|
||||
switch (type_entry->id) {
|
||||
case TypeTableEntryIdInvalid:
|
||||
zig_unreachable();
|
||||
case TypeTableEntryIdVoid:
|
||||
fprintf(irp->f, "%s\n", "void");
|
||||
fprintf(irp->f, "{}");
|
||||
break;
|
||||
case TypeTableEntryIdNumLitFloat:
|
||||
fprintf(irp->f, "%f\n", const_instruction->base.static_value.data.x_bignum.data.x_float);
|
||||
fprintf(irp->f, "%f", instruction->static_value.data.x_bignum.data.x_float);
|
||||
break;
|
||||
case TypeTableEntryIdNumLitInt:
|
||||
{
|
||||
BigNum *bignum = &const_instruction->base.static_value.data.x_bignum;
|
||||
BigNum *bignum = &instruction->static_value.data.x_bignum;
|
||||
const char *negative_str = bignum->is_negative ? "-" : "";
|
||||
fprintf(irp->f, "%s%llu\n", negative_str, bignum->data.x_uint);
|
||||
fprintf(irp->f, "%s%llu", negative_str, bignum->data.x_uint);
|
||||
break;
|
||||
}
|
||||
case TypeTableEntryIdMetaType:
|
||||
fprintf(irp->f, "%s\n", buf_ptr(&const_instruction->base.static_value.data.x_type->name));
|
||||
fprintf(irp->f, "%s", buf_ptr(&instruction->static_value.data.x_type->name));
|
||||
break;
|
||||
case TypeTableEntryIdInt:
|
||||
{
|
||||
BigNum *bignum = &const_instruction->base.static_value.data.x_bignum;
|
||||
BigNum *bignum = &instruction->static_value.data.x_bignum;
|
||||
assert(bignum->kind == BigNumKindInt);
|
||||
const char *negative_str = bignum->is_negative ? "-" : "";
|
||||
fprintf(irp->f, "%s%llu\n", negative_str, bignum->data.x_uint);
|
||||
fprintf(irp->f, "%s%llu", negative_str, bignum->data.x_uint);
|
||||
}
|
||||
break;
|
||||
case TypeTableEntryIdUnreachable:
|
||||
fprintf(irp->f, "@unreachable()");
|
||||
break;
|
||||
case TypeTableEntryIdVar:
|
||||
case TypeTableEntryIdBool:
|
||||
case TypeTableEntryIdUnreachable:
|
||||
case TypeTableEntryIdFloat:
|
||||
case TypeTableEntryIdPointer:
|
||||
case TypeTableEntryIdArray:
|
||||
@ -77,6 +75,27 @@ static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction)
|
||||
}
|
||||
}
|
||||
|
||||
static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
if (instruction->static_value.ok) {
|
||||
ir_print_const_instruction(irp, instruction);
|
||||
} else {
|
||||
fprintf(irp->f, "#%zu", instruction->debug_id);
|
||||
}
|
||||
}
|
||||
|
||||
static void ir_print_other_block(IrPrint *irp, IrBasicBlock *bb) {
|
||||
fprintf(irp->f, "$%s_%zu", bb->name_hint, bb->debug_id);
|
||||
}
|
||||
|
||||
static void ir_print_return(IrPrint *irp, IrInstructionReturn *return_instruction) {
|
||||
assert(return_instruction->value);
|
||||
fprintf(irp->f, "return ");
|
||||
ir_print_other_instruction(irp, return_instruction->value);
|
||||
}
|
||||
|
||||
static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction) {
|
||||
ir_print_const_instruction(irp, &const_instruction->base);
|
||||
}
|
||||
|
||||
static const char *ir_bin_op_id_str(IrBinOp op_id) {
|
||||
switch (op_id) {
|
||||
@ -169,87 +188,108 @@ static const char *ir_un_op_id_str(IrUnOp op_id) {
|
||||
}
|
||||
|
||||
static void ir_print_un_op(IrPrint *irp, IrInstructionUnOp *un_op_instruction) {
|
||||
ir_print_prefix(irp, &un_op_instruction->base);
|
||||
fprintf(irp->f, "%s #%zu\n",
|
||||
ir_un_op_id_str(un_op_instruction->op_id),
|
||||
un_op_instruction->value->debug_id);
|
||||
fprintf(irp->f, "%s ", ir_un_op_id_str(un_op_instruction->op_id));
|
||||
ir_print_other_instruction(irp, un_op_instruction->value);
|
||||
}
|
||||
|
||||
static void ir_print_bin_op(IrPrint *irp, IrInstructionBinOp *bin_op_instruction) {
|
||||
ir_print_prefix(irp, &bin_op_instruction->base);
|
||||
fprintf(irp->f, "#%zu %s #%zu\n",
|
||||
bin_op_instruction->op1->debug_id,
|
||||
ir_bin_op_id_str(bin_op_instruction->op_id),
|
||||
bin_op_instruction->op2->debug_id);
|
||||
ir_print_other_instruction(irp, bin_op_instruction->op1);
|
||||
fprintf(irp->f, " %s ", ir_bin_op_id_str(bin_op_instruction->op_id));
|
||||
ir_print_other_instruction(irp, bin_op_instruction->op2);
|
||||
}
|
||||
|
||||
static void ir_print_load_var(IrPrint *irp, IrInstructionLoadVar *load_var_instruction) {
|
||||
ir_print_prefix(irp, &load_var_instruction->base);
|
||||
fprintf(irp->f, "%s\n",
|
||||
buf_ptr(&load_var_instruction->var->name));
|
||||
fprintf(irp->f, "%s", buf_ptr(&load_var_instruction->var->name));
|
||||
}
|
||||
|
||||
static void ir_print_cast(IrPrint *irp, IrInstructionCast *cast_instruction) {
|
||||
ir_print_prefix(irp, &cast_instruction->base);
|
||||
fprintf(irp->f, "cast #%zu to #%zu\n",
|
||||
cast_instruction->value->debug_id,
|
||||
cast_instruction->dest_type->debug_id);
|
||||
fprintf(irp->f, "cast ");
|
||||
ir_print_other_instruction(irp, cast_instruction->value);
|
||||
fprintf(irp->f, " to ");
|
||||
ir_print_other_instruction(irp, cast_instruction->dest_type);
|
||||
}
|
||||
|
||||
static void ir_print_call(IrPrint *irp, IrInstructionCall *call_instruction) {
|
||||
ir_print_prefix(irp, &call_instruction->base);
|
||||
fprintf(irp->f, "#%zu(", call_instruction->fn->debug_id);
|
||||
ir_print_other_instruction(irp, call_instruction->fn);
|
||||
fprintf(irp->f, "(");
|
||||
for (size_t i = 0; i < call_instruction->arg_count; i += 1) {
|
||||
IrInstruction *arg = call_instruction->args[i];
|
||||
if (i != 0)
|
||||
fprintf(irp->f, ", ");
|
||||
fprintf(irp->f, "#%zu", arg->debug_id);
|
||||
ir_print_other_instruction(irp, arg);
|
||||
}
|
||||
fprintf(irp->f, ")\n");
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_builtin_call(IrPrint *irp, IrInstructionBuiltinCall *call_instruction) {
|
||||
ir_print_prefix(irp, &call_instruction->base);
|
||||
fprintf(irp->f, "@%s(", buf_ptr(&call_instruction->fn->name));
|
||||
for (size_t i = 0; i < call_instruction->fn->param_count; i += 1) {
|
||||
IrInstruction *arg = call_instruction->args[i];
|
||||
if (i != 0)
|
||||
fprintf(irp->f, ", ");
|
||||
fprintf(irp->f, "#%zu", arg->debug_id);
|
||||
ir_print_other_instruction(irp, arg);
|
||||
}
|
||||
fprintf(irp->f, ")\n");
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
|
||||
static void ir_print_cond_br(IrPrint *irp, IrInstructionCondBr *cond_br_instruction) {
|
||||
ir_print_prefix(irp, &cond_br_instruction->base);
|
||||
fprintf(irp->f, "if #%zu then $%s_%zu else $%s_%zu\n",
|
||||
cond_br_instruction->condition->debug_id,
|
||||
cond_br_instruction->then_block->name_hint, cond_br_instruction->then_block->debug_id,
|
||||
cond_br_instruction->else_block->name_hint, cond_br_instruction->else_block->debug_id);
|
||||
fprintf(irp->f, "if (");
|
||||
ir_print_other_instruction(irp, cond_br_instruction->condition);
|
||||
fprintf(irp->f, ") ");
|
||||
ir_print_other_block(irp, cond_br_instruction->then_block);
|
||||
fprintf(irp->f, " else ");
|
||||
ir_print_other_block(irp, cond_br_instruction->else_block);
|
||||
}
|
||||
|
||||
static void ir_print_br(IrPrint *irp, IrInstructionBr *br_instruction) {
|
||||
ir_print_prefix(irp, &br_instruction->base);
|
||||
fprintf(irp->f, "goto $%s_%zu\n",
|
||||
br_instruction->dest_block->name_hint, br_instruction->dest_block->debug_id);
|
||||
fprintf(irp->f, "goto ");
|
||||
ir_print_other_block(irp, br_instruction->dest_block);
|
||||
}
|
||||
|
||||
static void ir_print_phi(IrPrint *irp, IrInstructionPhi *phi_instruction) {
|
||||
ir_print_prefix(irp, &phi_instruction->base);
|
||||
for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) {
|
||||
IrBasicBlock *incoming_block = phi_instruction->incoming_blocks[i];
|
||||
IrInstruction *incoming_value = phi_instruction->incoming_values[i];
|
||||
if (i != 0)
|
||||
fprintf(irp->f, " ");
|
||||
fprintf(irp->f, "$%s_%zu:#%zu",
|
||||
incoming_block->name_hint, incoming_block->debug_id,
|
||||
incoming_value->debug_id);
|
||||
ir_print_other_block(irp, incoming_block);
|
||||
fprintf(irp->f, ":");
|
||||
ir_print_other_instruction(irp, incoming_value);
|
||||
}
|
||||
fprintf(irp->f, "\n");
|
||||
}
|
||||
|
||||
static void ir_print_container_init_list(IrPrint *irp, IrInstructionContainerInitList *instruction) {
|
||||
ir_print_other_instruction(irp, instruction->container_type);
|
||||
fprintf(irp->f, "{");
|
||||
for (size_t i = 0; i < instruction->item_count; i += 1) {
|
||||
IrInstruction *item = instruction->items[i];
|
||||
if (i != 0)
|
||||
fprintf(irp->f, ", ");
|
||||
ir_print_other_instruction(irp, item);
|
||||
}
|
||||
fprintf(irp->f, "}");
|
||||
}
|
||||
|
||||
static void ir_print_container_init_fields(IrPrint *irp, IrInstructionContainerInitFields *instruction) {
|
||||
ir_print_other_instruction(irp, instruction->container_type);
|
||||
fprintf(irp->f, "{");
|
||||
for (size_t i = 0; i < instruction->field_count; i += 1) {
|
||||
Buf *name = instruction->field_names[i];
|
||||
IrInstruction *field_value = instruction->field_values[i];
|
||||
const char *comma = (i == 0) ? "" : ", ";
|
||||
fprintf(irp->f, "%s.%s = ", comma, buf_ptr(name));
|
||||
ir_print_other_instruction(irp, field_value);
|
||||
}
|
||||
fprintf(irp->f, "}");
|
||||
}
|
||||
|
||||
static void ir_print_unreachable(IrPrint *irp, IrInstructionUnreachable *instruction) {
|
||||
fprintf(irp->f, "unreachable");
|
||||
}
|
||||
|
||||
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
ir_print_prefix(irp, instruction);
|
||||
switch (instruction->id) {
|
||||
case IrInstructionIdInvalid:
|
||||
zig_unreachable();
|
||||
@ -286,10 +326,20 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdPhi:
|
||||
ir_print_phi(irp, (IrInstructionPhi *)instruction);
|
||||
break;
|
||||
case IrInstructionIdContainerInitList:
|
||||
ir_print_container_init_list(irp, (IrInstructionContainerInitList *)instruction);
|
||||
break;
|
||||
case IrInstructionIdContainerInitFields:
|
||||
ir_print_container_init_fields(irp, (IrInstructionContainerInitFields *)instruction);
|
||||
break;
|
||||
case IrInstructionIdUnreachable:
|
||||
ir_print_unreachable(irp, (IrInstructionUnreachable *)instruction);
|
||||
break;
|
||||
case IrInstructionIdSwitchBr:
|
||||
case IrInstructionIdStoreVar:
|
||||
zig_panic("TODO print more IR instructions");
|
||||
}
|
||||
fprintf(irp->f, "\n");
|
||||
}
|
||||
|
||||
void ir_print(FILE *f, IrExecutable *executable, int indent_size) {
|
||||
|
@ -1912,14 +1912,11 @@ static AstNode *ast_parse_label(ParseContext *pc, size_t *token_index, bool mand
|
||||
return node;
|
||||
}
|
||||
|
||||
//static AstNode *ast_create_void_expr(ParseContext *pc, Token *token) {
|
||||
// AstNode *node = ast_create_node(pc, NodeTypeContainerInitExpr, token);
|
||||
// node->data.container_init_expr.type = ast_create_node(pc, NodeTypeSymbol, token);
|
||||
// node->data.container_init_expr.kind = ContainerInitKindArray;
|
||||
// node->data.container_init_expr.type->data.symbol_expr.symbol = pc->void_buf;
|
||||
// normalize_parent_ptrs(node);
|
||||
// return node;
|
||||
//}
|
||||
static AstNode *ast_create_void_expr(ParseContext *pc, Token *token) {
|
||||
AstNode *node = ast_create_node(pc, NodeTypeBlock, token);
|
||||
normalize_parent_ptrs(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace)
|
||||
@ -1961,12 +1958,13 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand
|
||||
semicolon_expected = !statement_node;
|
||||
if (!statement_node) {
|
||||
statement_node = ast_parse_non_block_expr(pc, token_index, false);
|
||||
if (!statement_node) {
|
||||
statement_node = ast_create_void_expr(pc, last_token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (statement_node) {
|
||||
node->data.block.statements.append(statement_node);
|
||||
}
|
||||
node->data.block.statements.append(statement_node);
|
||||
|
||||
last_token = &pc->tokens->at(*token_index);
|
||||
if (last_token->id == TokenIdRBrace) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user