From 0e92166d442b120b06db57623a93c6ec43e3fccb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 3 Aug 2016 14:17:13 +0100 Subject: [PATCH] Slice creation for SliceExpr --- examples/test.ll | 23 +++- examples/test.odin | 3 + run.bat | 6 +- src/checker/checker.cpp | 10 +- src/checker/expression.cpp | 54 ++++---- src/checker/statements.cpp | 36 +++--- src/checker/type.cpp | 23 ++++ src/codegen/codegen.cpp | 8 ++ src/codegen/print.cpp | 21 ++-- src/codegen/ssa.cpp | 245 ++++++++++++++++++++++++++++++------- 10 files changed, 317 insertions(+), 112 deletions(-) diff --git a/examples/test.ll b/examples/test.ll index a66808824..295c13666 100644 --- a/examples/test.ll +++ b/examples/test.ll @@ -1,6 +1,23 @@ -; ModuleID = '..\examples/test.bc' - define void @main() { "entry - 0": - ret void + %0 = alloca [16 x i64], align 8 ; a + store [16 x i64] zeroinitializer, [16 x i64]* %0 + %1 = alloca {i64*, i64, i64}, align 8 ; b + store {i64*, i64, i64} zeroinitializer, {i64*, i64, i64}* %1 + %2 = sub i64 1, 0 + %3 = sub i64 2, 0 + %4 = getelementptr inbounds [16 x i64], [16 x i64]* %0, i64 0, i64 0 + %5 = getelementptr i64, i64* %4, i64 0 + %6 = alloca {i64*, i64, i64}, align 8 + store {i64*, i64, i64} zeroinitializer, {i64*, i64, i64}* %6 + %7 = getelementptr inbounds {i64*, i64, i64}, {i64*, i64, i64}* %6, i64 0, i32 0 + store i64* %5, i64** %7 + %8 = getelementptr inbounds {i64*, i64, i64}, {i64*, i64, i64}* %6, i64 0, i32 1 + store i64 %2, i64* %8 + %9 = getelementptr inbounds {i64*, i64, i64}, {i64*, i64, i64}* %6, i64 0, i32 2 + store i64 %3, i64* %9 + %10 = load {i64*, i64, i64}, {i64*, i64, i64}* %6 + store {i64*, i64, i64} %10, {i64*, i64, i64}* %1 + ret void } + diff --git a/examples/test.odin b/examples/test.odin index fd7194210..a2054dcff 100644 --- a/examples/test.odin +++ b/examples/test.odin @@ -1,2 +1,5 @@ main :: proc() { + a : [16]int; + b := a[0:1:2]; + } diff --git a/run.bat b/run.bat index 2ed5d13f0..e5f1b6d51 100644 --- a/run.bat +++ b/run.bat @@ -3,6 +3,6 @@ rem del "..\examples\test.bc" call ..\bin\odin.exe ..\examples/test.odin && lli ..\examples/test.ll -call opt -mem2reg ..\examples/test.ll > ..\examples/test.bc -call llvm-dis ..\examples/test.bc -o ..\examples/test.ll -call clang ..\examples/test.c -O0 -S -emit-llvm -o ..\examples/test-c.ll +rem call opt -mem2reg ..\examples/test.ll > ..\examples/test.bc +rem call llvm-dis ..\examples/test.bc -o ..\examples/test.ll +rem call clang ..\examples/test.c -O0 -S -emit-llvm -o ..\examples/test-c.ll diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index 5a03baa76..884032ba9 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -312,16 +312,16 @@ void init_universal_scope(void) { } // Constants - add_global_constant(a, make_string("true"), &basic_types[Basic_UntypedBool], make_exact_value_bool(true)); - add_global_constant(a, make_string("false"), &basic_types[Basic_UntypedBool], make_exact_value_bool(false)); - add_global_constant(a, make_string("null"), &basic_types[Basic_UntypedPointer], make_exact_value_pointer(NULL)); + add_global_constant(a, make_string("true"), t_untyped_bool, make_exact_value_bool(true)); + add_global_constant(a, make_string("false"), t_untyped_bool, make_exact_value_bool(false)); + add_global_constant(a, make_string("null"), t_untyped_pointer, make_exact_value_pointer(NULL)); // Builtin Procedures for (isize i = 0; i < gb_count_of(builtin_procedures); i++) { BuiltinProcedureId id = cast(BuiltinProcedureId)i; Token token = {Token_Identifier}; token.string = builtin_procedures[i].name; - Entity *entity = alloc_entity(a, Entity_Builtin, NULL, token, &basic_types[Basic_Invalid]); + Entity *entity = alloc_entity(a, Entity_Builtin, NULL, token, t_invalid); entity->builtin.id = id; add_global_entity(entity); } @@ -433,7 +433,7 @@ void add_type_and_value(CheckerInfo *i, AstNode *expression, AddressingMode mode if (mode == Addressing_Constant) { GB_ASSERT(value.kind != ExactValue_Invalid); - GB_ASSERT(type == &basic_types[Basic_Invalid] || is_type_constant_type(type)); + GB_ASSERT(type == t_invalid || is_type_constant_type(type)); } TypeAndValue tv = {}; diff --git a/src/checker/expression.cpp b/src/checker/expression.cpp index 2fe95a8e3..37fa071e3 100644 --- a/src/checker/expression.cpp +++ b/src/checker/expression.cpp @@ -167,7 +167,7 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type) { switch (e->kind) { case Entity_Constant: add_declaration_dependency(c, e); - if (e->type == &basic_types[Basic_Invalid]) + if (e->type == t_invalid) return; o->value = e->constant.value; GB_ASSERT(o->value.kind != ExactValue_Invalid); @@ -177,7 +177,7 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type) { case Entity_Variable: add_declaration_dependency(c, e); e->variable.used = true; - if (e->type == &basic_types[Basic_Invalid]) + if (e->type == t_invalid) return; o->mode = Addressing_Variable; break; @@ -306,7 +306,7 @@ Type *check_type_expr_extra(Checker *c, AstNode *e, Type *named_type) { break; } - Type *t = &basic_types[Basic_Invalid]; + Type *t = t_invalid; set_base_type(named_type, t); return t; } @@ -396,7 +396,7 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type) { break; } - type = &basic_types[Basic_Invalid]; + type = t_invalid; set_base_type(named_type, type); end: @@ -672,7 +672,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) { // TODO(bill): What should I do? } - x->type = &basic_types[Basic_UntypedBool]; + x->type = t_untyped_bool; } void check_binary_expr(Checker *c, Operand *x, AstNode *node) { @@ -707,8 +707,8 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) { } if (!are_types_identical(x->type, y->type)) { - if (x->type != &basic_types[Basic_Invalid] && - y->type != &basic_types[Basic_Invalid]) { + if (x->type != t_invalid && + y->type != t_invalid) { gbString xt = type_to_string(x->type); gbString yt = type_to_string(y->type); defer (gb_string_free(xt)); @@ -833,7 +833,7 @@ void convert_to_typed(Checker *c, Operand *operand, Type *target_type) { GB_ASSERT_NOT_NULL(target_type); if (operand->mode == Addressing_Invalid || is_type_typed(operand->type) || - target_type == &basic_types[Basic_Invalid]) { + target_type == t_invalid) { return; } @@ -883,7 +883,7 @@ void convert_to_typed(Checker *c, Operand *operand, Type *target_type) { case Type_Pointer: switch (operand->type->basic.kind) { case Basic_UntypedPointer: - target_type = &basic_types[Basic_UntypedPointer]; + target_type = t_untyped_pointer; break; default: convert_untyped_error(c, operand, target_type); @@ -907,7 +907,7 @@ b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *valu return false; } - convert_to_typed(c, &operand, &basic_types[Basic_int]); + convert_to_typed(c, &operand, t_int); if (operand.mode == Addressing_Invalid) { if (value) *value = 0; return false; @@ -1052,7 +1052,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) operand->mode = Addressing_Constant; operand->value = make_exact_value_integer(type_size_of(c->sizes, c->allocator, type)); - operand->type = &basic_types[Basic_int]; + operand->type = t_int; } break; @@ -1064,7 +1064,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) operand->mode = Addressing_Constant; operand->value = make_exact_value_integer(type_size_of(c->sizes, c->allocator, operand->type)); - operand->type = &basic_types[Basic_int]; + operand->type = t_int; break; case BuiltinProcedure_align_of: { @@ -1076,7 +1076,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } operand->mode = Addressing_Constant; operand->value = make_exact_value_integer(type_align_of(c->sizes, c->allocator, type)); - operand->type = &basic_types[Basic_int]; + operand->type = t_int; } break; case BuiltinProcedure_align_of_val: @@ -1087,7 +1087,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) operand->mode = Addressing_Constant; operand->value = make_exact_value_integer(type_align_of(c->sizes, c->allocator, operand->type)); - operand->type = &basic_types[Basic_int]; + operand->type = t_int; break; case BuiltinProcedure_offset_of: { @@ -1118,7 +1118,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) operand->mode = Addressing_Constant; operand->value = make_exact_value_integer(type_offset_of(c->sizes, c->allocator, type, index)); - operand->type = &basic_types[Basic_int]; + operand->type = t_int; } break; case BuiltinProcedure_offset_of_val: { @@ -1154,7 +1154,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) operand->mode = Addressing_Constant; operand->value = make_exact_value_integer(type_offset_of(c->sizes, c->allocator, type, index)); - operand->type = &basic_types[Basic_int]; + operand->type = t_int; } break; case BuiltinProcedure_static_assert: @@ -1220,7 +1220,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } operand->mode = mode; - operand->type = &basic_types[Basic_int]; + operand->type = t_int; operand->value = value; } break; @@ -1262,7 +1262,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) return false; } - operand->type = &basic_types[Basic_int]; // Returns number of elements copied + operand->type = t_int; // Returns number of elements copied operand->mode = Addressing_Value; } break; @@ -1507,7 +1507,7 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ ExpressionKind kind = Expression_Statement; o->mode = Addressing_Invalid; - o->type = &basic_types[Basic_Invalid]; + o->type = t_invalid; switch (node->kind) { case_ast_node(be, BadExpr, node) @@ -1519,16 +1519,16 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ case_end; case_ast_node(bl, BasicLit, node); - BasicKind basic_kind = Basic_Invalid; + Type *t = t_invalid; switch (bl->kind) { - case Token_Integer: basic_kind = Basic_UntypedInteger; break; - case Token_Float: basic_kind = Basic_UntypedFloat; break; - case Token_String: basic_kind = Basic_UntypedString; break; - case Token_Rune: basic_kind = Basic_UntypedRune; break; + case Token_Integer: t = t_untyped_integer; break; + case Token_Float: t = t_untyped_float; break; + case Token_String: t = t_untyped_string; break; + case Token_Rune: t = t_untyped_rune; break; default: GB_PANIC("Unknown literal"); break; } o->mode = Addressing_Constant; - o->type = &basic_types[basic_kind]; + o->type = t; o->value = make_exact_value_from_basic_literal(*bl); case_end; @@ -1684,7 +1684,7 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ max_count = o->value.value_string.len; } o->mode = Addressing_Value; - o->type = &basic_types[Basic_u8]; + o->type = t_u8; } break; @@ -1866,7 +1866,7 @@ ExpressionKind check_expr_base(Checker *c, Operand *o, AstNode *node, Type *type ExactValue value = {ExactValue_Invalid}; switch (o->mode) { case Addressing_Invalid: - type = &basic_types[Basic_Invalid]; + type = t_invalid; break; case Addressing_NoValue: type = NULL; diff --git a/src/checker/statements.cpp b/src/checker/statements.cpp index 8910e7d3d..b1acd7cd5 100644 --- a/src/checker/statements.cpp +++ b/src/checker/statements.cpp @@ -73,7 +73,7 @@ b32 check_is_terminating(Checker *c, AstNode *node) { b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type) { if (operand->mode == Addressing_Invalid || - type == &basic_types[Basic_Invalid]) { + type == t_invalid) { return true; } @@ -166,7 +166,7 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) { if (op_a->mode == Addressing_Invalid || - op_a->type == &basic_types[Basic_Invalid]) { + op_a->type == t_invalid) { return NULL; } @@ -200,7 +200,7 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) { if (e) e->variable.used = used; if (op_b.mode == Addressing_Invalid || - op_b.type == &basic_types[Basic_Invalid]) { + op_b.type == t_invalid) { return NULL; } @@ -233,10 +233,10 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) { // NOTE(bill): `content_name` is for debugging Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String context_name) { if (operand->mode == Addressing_Invalid || - operand->type == &basic_types[Basic_Invalid] || - e->type == &basic_types[Basic_Invalid]) { + operand->type == t_invalid || + e->type == t_invalid) { if (e->type == NULL) - e->type = &basic_types[Basic_Invalid]; + e->type = t_invalid; return NULL; } @@ -244,9 +244,9 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex // NOTE(bill): Use the type of the operand Type *t = operand->type; if (is_type_untyped(t)) { - if (t == &basic_types[Basic_Invalid]) { + if (t == t_invalid) { error(&c->error_collector, e->token, "Use of untyped thing in %.*s", LIT(context_name)); - e->type = &basic_types[Basic_Invalid]; + e->type = t_invalid; return NULL; } t = default_type(t); @@ -295,10 +295,10 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNode *in void check_init_constant(Checker *c, Entity *e, Operand *operand) { if (operand->mode == Addressing_Invalid || - operand->type == &basic_types[Basic_Invalid] || - e->type == &basic_types[Basic_Invalid]) { + operand->type == t_invalid || + e->type == t_invalid) { if (e->type == NULL) - e->type = &basic_types[Basic_Invalid]; + e->type = t_invalid; return; } @@ -307,7 +307,7 @@ void check_init_constant(Checker *c, Entity *e, Operand *operand) { error(&c->error_collector, ast_node_token(operand->expr), "`%.*s` is not a constant", LIT(ast_node_token(operand->expr).string)); if (e->type == NULL) - e->type = &basic_types[Basic_Invalid]; + e->type = t_invalid; return; } if (!is_type_constant_type(operand->type)) { @@ -330,7 +330,7 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_e GB_ASSERT(e->type == NULL); if (e->variable.visited) { - e->type = &basic_types[Basic_Invalid]; + e->type = t_invalid; return; } e->variable.visited = true; @@ -342,7 +342,7 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_e defer (gb_string_free(str)); error(&c->error_collector, ast_node_token(type_expr), "Invalid constant type `%s`", str); - e->type = &basic_types[Basic_Invalid]; + e->type = t_invalid; return; } e->type = t; @@ -464,7 +464,7 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count GB_ASSERT(e->kind == Entity_Variable); if (e->variable.visited) { - e->type = &basic_types[Basic_Invalid]; + e->type = t_invalid; return; } e->variable.visited = true; @@ -474,7 +474,7 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count if (init_expr == NULL) { if (type_expr == NULL) - e->type = &basic_types[Basic_Invalid]; + e->type = t_invalid; return; } @@ -815,14 +815,14 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { if (vd->type) { init_type = check_type(c, vd->type, NULL); if (init_type == NULL) - init_type = &basic_types[Basic_Invalid]; + init_type = t_invalid; } for (isize i = 0; i < entity_count; i++) { Entity *e = entities[i]; GB_ASSERT(e != NULL); if (e->variable.visited) { - e->type = &basic_types[Basic_Invalid]; + e->type = t_invalid; continue; } e->variable.visited = true; diff --git a/src/checker/type.cpp b/src/checker/type.cpp index 37d37162d..e2506b3a2 100644 --- a/src/checker/type.cpp +++ b/src/checker/type.cpp @@ -246,6 +246,29 @@ gb_global Type basic_type_aliases[] = { {Type_Basic, {Basic_rune, BasicFlag_Integer, STR_LIT("rune")}}, }; +gb_global Type *t_invalid = &basic_types[Basic_Invalid]; +gb_global Type *t_bool = &basic_types[Basic_bool]; +gb_global Type *t_i8 = &basic_types[Basic_i8]; +gb_global Type *t_i16 = &basic_types[Basic_i16]; +gb_global Type *t_i32 = &basic_types[Basic_i32]; +gb_global Type *t_i64 = &basic_types[Basic_i64]; +gb_global Type *t_u8 = &basic_types[Basic_u8]; +gb_global Type *t_u16 = &basic_types[Basic_u16]; +gb_global Type *t_u32 = &basic_types[Basic_u32]; +gb_global Type *t_u64 = &basic_types[Basic_u64]; +gb_global Type *t_f32 = &basic_types[Basic_f32]; +gb_global Type *t_f64 = &basic_types[Basic_f64]; +gb_global Type *t_int = &basic_types[Basic_int]; +gb_global Type *t_uint = &basic_types[Basic_uint]; +gb_global Type *t_rawptr = &basic_types[Basic_rawptr]; +gb_global Type *t_string = &basic_types[Basic_string]; +gb_global Type *t_untyped_bool = &basic_types[Basic_UntypedBool]; +gb_global Type *t_untyped_integer = &basic_types[Basic_UntypedInteger]; +gb_global Type *t_untyped_float = &basic_types[Basic_UntypedFloat]; +gb_global Type *t_untyped_pointer = &basic_types[Basic_UntypedPointer]; +gb_global Type *t_untyped_string = &basic_types[Basic_UntypedString]; +gb_global Type *t_untyped_rune = &basic_types[Basic_UntypedRune]; + b32 is_type_named(Type *t) { if (t->kind == Type_Basic) diff --git a/src/codegen/codegen.cpp b/src/codegen/codegen.cpp index 7e9397580..f799497ca 100644 --- a/src/codegen/codegen.cpp +++ b/src/codegen/codegen.cpp @@ -33,6 +33,14 @@ void ssa_gen_destroy(ssaGen *s) { } void ssa_gen_code(ssaGen *s) { + if (v_zero == NULL) { + v_zero = ssa_make_value_constant(gb_heap_allocator(), t_int, make_exact_value_integer(0)); + v_one = ssa_make_value_constant(gb_heap_allocator(), t_int, make_exact_value_integer(1)); + v_zero32 = ssa_make_value_constant(gb_heap_allocator(), t_i32, make_exact_value_integer(0)); + v_one32 = ssa_make_value_constant(gb_heap_allocator(), t_i32, make_exact_value_integer(1)); + v_two32 = ssa_make_value_constant(gb_heap_allocator(), t_i32, make_exact_value_integer(2)); + } + ssaModule *m = &s->module; CheckerInfo *info = m->info; gbAllocator a = m->allocator; diff --git a/src/codegen/print.cpp b/src/codegen/print.cpp index 7991c9310..d8e43766c 100644 --- a/src/codegen/print.cpp +++ b/src/codegen/print.cpp @@ -104,7 +104,7 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) { case Type_Slice: ssa_fprintf(f, "{"); ssa_print_type(f, s, t->slice.element); - ssa_fprintf(f, "*, %lld, %lld}", word_bits, word_bits); + ssa_fprintf(f, "*, i%lld, i%lld}", word_bits, word_bits); break; case Type_Structure: ssa_fprintf(f, "{"); @@ -158,7 +158,7 @@ void ssa_print_exact_value(gbFile *f, ssaModule *m, ExactValue value, Type *type break; case ExactValue_String: { ssa_fprintf(f, "{"); - ssa_print_type(f, m->sizes, &basic_types[Basic_i8]); + ssa_print_type(f, m->sizes, t_i8); ssa_fprintf(f, "* c\""); // TODO(bill): Make unquote string function String unquoted = value.value_string; @@ -166,7 +166,7 @@ void ssa_print_exact_value(gbFile *f, ssaModule *m, ExactValue value, Type *type unquoted.len -= 2; ssa_print_escape_string(f, unquoted); ssa_fprintf(f, "\", "); - ssa_print_type(f, m->sizes, &basic_types[Basic_int]); + ssa_print_type(f, m->sizes, t_int); ssa_fprintf(f, " %td}", value.value_string.len); } break; case ExactValue_Integer: @@ -246,7 +246,11 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) { ssa_fprintf(f, "%%%d = alloca ", value->id); ssa_print_type(f, m->sizes, type); ssa_fprintf(f, ", align %lld ", type_align_of(m->sizes, gb_heap_allocator(), type)); - ssa_fprintf(f, "; %.*s", LIT(instr->local.entity->token.string)); + { + String str = instr->local.entity->token.string; + if (str.len > 0) + ssa_fprintf(f, "; %.*s", LIT(instr->local.entity->token.string)); + } ssa_fprintf(f, "\n"); ssa_fprintf(f, "\tstore "); ssa_print_type(f, m->sizes, type); @@ -281,7 +285,6 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) { case ssaInstr_GetElementPtr: { Type *et = instr->get_element_ptr.element_type; - Type *t_int = &basic_types[Basic_int]; ssa_fprintf(f, "%%%d = getelementptr ", value->id); if (instr->get_element_ptr.inbounds) ssa_fprintf(f, "inbounds "); @@ -292,10 +295,12 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) { ssa_fprintf(f, "* "); ssa_print_value(f, m, instr->get_element_ptr.address, et); for (isize i = 0; i < instr->get_element_ptr.index_count; i++) { + ssaValue *index = instr->get_element_ptr.indices[i]; + Type *t = ssa_value_type(index); ssa_fprintf(f, ", "); - ssa_print_type(f, m->sizes, t_int); + ssa_print_type(f, m->sizes, t); ssa_fprintf(f, " "); - ssa_print_value(f, m, instr->get_element_ptr.indices[i], t_int); + ssa_print_value(f, m, index, t); } ssa_fprintf(f, "\n"); } break; @@ -303,7 +308,6 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) { case ssaInstr_Br: { ssa_fprintf(f, "br "); if (instr->br.cond != NULL) { - Type *t_bool = &basic_types[Basic_bool]; ssa_print_type(f, m->sizes, t_bool); ssa_fprintf(f, " "); ssa_print_value(f, m, instr->br.cond, t_bool); @@ -495,7 +499,6 @@ void ssa_print_llvm_ir(gbFile *f, ssaModule *m) { ssa_print_instr(f, m, value); } } - ssa_fprintf(f, "}\n\n"); } diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index d587000b5..4d0dd29a5 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -190,6 +190,12 @@ struct ssaValue { }; }; +gb_global ssaValue *v_zero = NULL; +gb_global ssaValue *v_one = NULL; +gb_global ssaValue *v_zero32 = NULL; +gb_global ssaValue *v_one32 = NULL; +gb_global ssaValue *v_two32 = NULL; + enum ssaLvalueKind { ssaLvalue_Blank, ssaLvalue_Address, @@ -208,6 +214,10 @@ struct ssaLvalue { }; }; + + + + ssaLvalue ssa_make_lvalue_address(ssaValue *value, AstNode *expr) { ssaLvalue lval = {ssaLvalue_Address}; lval.address.value = value; @@ -455,6 +465,7 @@ ssaValue *ssa_make_instr_ret(ssaProcedure *p, ssaValue *value) { + ssaValue *ssa_make_value_constant(gbAllocator a, Type *type, ExactValue value) { ssaValue *v = ssa_alloc_value(a, ssaValue_Constant); v->constant.type = type; @@ -525,16 +536,13 @@ b32 ssa_is_blank_ident(AstNode *node) { -ssaValue *ssa_block_emit(ssaBlock *b, ssaValue *instr) { +ssaValue *ssa_emit(ssaProcedure *proc, ssaValue *instr) { + ssaBlock *b = proc->curr_block; instr->instr.parent = b; if (b) { gb_array_append(b->instrs, instr); } return instr; - -} -ssaValue *ssa_emit(ssaProcedure *proc, ssaValue *instr) { - return ssa_block_emit(proc->curr_block, instr); } ssaValue *ssa_emit_store(ssaProcedure *p, ssaValue *address, ssaValue *value) { return ssa_emit(p, ssa_make_instr_store(p, address, value)); @@ -785,7 +793,127 @@ ssaValue *ssa_emit_comp(ssaProcedure *proc, Token op, ssaValue *left, ssaValue * return ssa_emit(proc, v); } +ssaValue *ssa_emit_ptr_offset(ssaProcedure *proc, ssaValue *ptr, ssaValue *offset) { + Type *type = ssa_value_type(ptr); + ssaValue *gep = NULL; + offset = ssa_emit_conv(proc, offset, t_int); + gep = ssa_make_instr_get_element_ptr(proc, ptr, offset, NULL, 1, false); + gep->instr.get_element_ptr.element_type = type_deref(type); + gep->instr.get_element_ptr.result_type = type; + return ssa_emit(proc, gep); +} +ssaValue *ssa_emit_struct_gep(ssaProcedure *proc, ssaValue *s, ssaValue *index, Type *result_type) { + ssaValue *gep = NULL; + // NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32 + index = ssa_emit_conv(proc, index, t_i32); + gep = ssa_make_instr_get_element_ptr(proc, s, v_zero, index, 2, true); + gep->instr.get_element_ptr.element_type = ssa_value_type(s); + gep->instr.get_element_ptr.result_type = result_type; + + return ssa_emit(proc, gep); +} + + +ssaValue *ssa_array_elem(ssaProcedure *proc, ssaValue *array) { + Type *t = ssa_value_type(array); + GB_ASSERT(t->kind == Type_Array); + Type *base_type = t->array.element; + ssaValue *elem = ssa_make_instr_get_element_ptr(proc, array, v_zero, v_zero, 2, true); + Type *result_type = make_type_pointer(proc->module->allocator, base_type); + elem->instr.get_element_ptr.element_type = t; + elem->instr.get_element_ptr.result_type = result_type; + return ssa_emit(proc, elem); +} +ssaValue *ssa_array_len(ssaProcedure *proc, ssaValue *array) { + Type *t = ssa_value_type(array); + GB_ASSERT(t->kind == Type_Array); + return ssa_make_value_constant(proc->module->allocator, t_int, make_exact_value_integer(t->array.count)); +} +ssaValue *ssa_array_cap(ssaProcedure *proc, ssaValue *array) { + return ssa_array_len(proc, array); +} + +ssaValue *ssa_slice_elem(ssaProcedure *proc, ssaValue *slice) { + Type *t = ssa_value_type(slice); + GB_ASSERT(t->kind == Type_Slice); + + Type *result_type = make_type_pointer(proc->module->allocator, t->slice.element); + return ssa_emit_load(proc, ssa_emit_struct_gep(proc, slice, v_zero32, result_type)); +} +ssaValue *ssa_slice_len(ssaProcedure *proc, ssaValue *slice) { + Type *t = ssa_value_type(slice); + GB_ASSERT(t->kind == Type_Slice); + return ssa_emit_load(proc, ssa_emit_struct_gep(proc, slice, v_one32, t_int)); +} +ssaValue *ssa_slice_cap(ssaProcedure *proc, ssaValue *slice) { + Type *t = ssa_value_type(slice); + GB_ASSERT(t->kind == Type_Slice); + return ssa_emit_load(proc, ssa_emit_struct_gep(proc, slice, v_two32, t_int)); +} + + + + + +ssaValue *ssa_emit_slice(ssaProcedure *proc, Type *slice_type, ssaValue *base, ssaValue *low, ssaValue *high, ssaValue *max) { + // TODO(bill): array bounds checking for slice creation + // TODO(bill): check that low < high <= max + gbAllocator a = proc->module->allocator; + Type *base_type = get_base_type(ssa_value_type(base)); + + if (low == NULL) { + low = v_zero; + } + if (high == NULL) { + switch (base_type->kind) { + case Type_Array: high = ssa_array_len(proc, base); break; + case Type_Slice: high = ssa_slice_len(proc, base); break; + case Type_Pointer: high = v_one; break; + } + } + if (max == NULL) { + switch (base_type->kind) { + case Type_Array: max = ssa_array_cap(proc, base); break; + case Type_Slice: max = ssa_slice_cap(proc, base); break; + case Type_Pointer: max = high; break; + } + } + GB_ASSERT(max != NULL); + + Token op_sub = {Token_Sub}; + ssaValue *len = ssa_emit_arith(proc, op_sub, high, low, t_int); + ssaValue *cap = ssa_emit_arith(proc, op_sub, max, low, t_int); + + ssaValue *elem = NULL; + switch (base_type->kind) { + case Type_Array: elem = ssa_array_elem(proc, base); break; + case Type_Slice: elem = ssa_slice_elem(proc, base); break; + case Type_Pointer: elem = base; break; + } + + elem = ssa_emit_ptr_offset(proc, elem, low); + + // NOTE(bill): Just used as dummy entity - never to be used really + Entity *slice_entity = make_entity_variable(proc->module->allocator, + proc->curr_block->scope, + empty_token, + slice_type); + + ssaValue *slice = ssa_emit(proc, ssa_make_instr_local(proc, slice_entity)); + + ssaValue *gep = NULL; + gep = ssa_emit_struct_gep(proc, slice, v_zero32, ssa_value_type(elem)); + ssa_emit_store(proc, gep, elem); + + gep = ssa_emit_struct_gep(proc, slice, v_one32, t_int); + ssa_emit_store(proc, gep, len); + + gep = ssa_emit_struct_gep(proc, slice, v_two32, t_int); + ssa_emit_store(proc, gep, cap); + + return ssa_emit_load(proc, slice); +} ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue *tv) { @@ -818,6 +946,10 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue return ssa_lvalue_load(ssa_build_addr(proc, expr), proc); case_end; + case_ast_node(se, SelectorExpr, expr); + return ssa_lvalue_load(ssa_build_addr(proc, expr), proc); + case_end; + case_ast_node(ue, UnaryExpr, expr); switch (ue->op.kind) { case Token_Pointer: @@ -826,8 +958,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue return ssa_build_expr(proc, ue->expr); case Token_Sub: { // NOTE(bill): -`x` == 0 - `x` - ExactValue zero = make_exact_value_integer(0); - ssaValue *left = ssa_make_value_constant(proc->module->allocator, tv->type, zero); + ssaValue *left = v_zero; ssaValue *right = ssa_build_expr(proc, ue->expr); return ssa_emit_arith(proc, ue->op, left, right, tv->type); } break; @@ -876,6 +1007,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue default: GB_PANIC("Invalid binary expression"); + break; } case_end; @@ -892,7 +1024,25 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue case_end; case_ast_node(se, SliceExpr, expr); - GB_PANIC("TODO(bill): ssa_build_single_expr SliceExpr"); + ssaValue *base = NULL; + ssaValue *low = NULL; + ssaValue *high = NULL; + ssaValue *max = NULL; + switch (tv->type->kind) { + case Type_Slice: + case Type_Array: + base = ssa_lvalue_address(ssa_build_addr(proc, se->expr), proc); + break; + case Type_Basic: + GB_PANIC("SliceExpr Type_Basic"); + break; + } + + if (se->low != NULL) low = ssa_build_expr(proc, se->low); + if (se->high != NULL) high = ssa_build_expr(proc, se->high); + if (se->triple_indexed) max = ssa_build_expr(proc, se->max); + + return ssa_emit_slice(proc, tv->type, base, low, high, max); case_end; case_ast_node(ie, IndexExpr, expr); @@ -911,10 +1061,6 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue } break; } case_end; - - case_ast_node(se, SelectorExpr, expr); - return ssa_build_expr(proc, se->selector); - case_end; } GB_PANIC("Unexpected expression"); @@ -968,59 +1114,67 @@ ssaLvalue ssa_build_addr(ssaProcedure *proc, AstNode *expr) { case_end; case_ast_node(se, SelectorExpr, expr); + AstNode *selector = unparen_expr(se->selector); + Type *type = type_of_expr(proc->module->info, se->expr); + + isize index = 0; + Entity *entity = lookup_field(type, selector, &index); + GB_ASSERT(entity != NULL); + + ssaValue *v = ssa_lvalue_address(ssa_build_addr(proc, se->expr), proc); + + if (type->kind == Type_Pointer) { + // NOTE(bill): Allow x^.y and x.y to be the same + type = type_deref(type); + v = ssa_emit_load(proc, v); + } + + ssaValue *i0 = v_zero32; + ssaValue *i1 = ssa_make_value_constant(proc->module->allocator, t_i32, make_exact_value_integer(index)); + ssaValue *gep = ssa_make_instr_get_element_ptr(proc, v, i0, i1, 2, true); + gep->instr.get_element_ptr.result_type = entity->type; + gep->instr.get_element_ptr.element_type = type; + v = ssa_emit(proc, gep); + return ssa_make_lvalue_address(v, expr); case_end; case_ast_node(ie, IndexExpr, expr); - Type *t_int = &basic_types[Basic_int]; ssaValue *v = NULL; - Type *element_type = NULL; - Type *t = type_of_expr(proc->module->info, ie->expr); - t = get_base_type(t); + Type *t = get_base_type(type_of_expr(proc->module->info, ie->expr)); switch (t->kind) { case Type_Array: { - ssaValue *e = ssa_lvalue_address(ssa_build_addr(proc, ie->expr), proc); - ssaValue *i0 = ssa_make_value_constant(proc->module->allocator, t_int, make_exact_value_integer(0)); - ssaValue *i1 = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); - ssaValue *gep = ssa_make_instr_get_element_ptr(proc, e, - i0, i1, 2, true); - element_type = t->array.element; - v = gep; + ssaValue *array = ssa_lvalue_address(ssa_build_addr(proc, ie->expr), proc); + ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); + ssaValue *elem = ssa_array_elem(proc, array); + v = ssa_emit_ptr_offset(proc, elem, index); } break; case Type_Pointer: { - ssaValue *e = ssa_lvalue_address(ssa_build_addr(proc, ie->expr), proc); - ssaValue *load = ssa_emit_load(proc, e); - gb_printf("load: %s\n", type_to_string(ssa_value_type(load))); + ssaValue *ptr = ssa_emit_load(proc, ssa_lvalue_address(ssa_build_addr(proc, ie->expr), proc)); ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); - ssaValue *gep = ssa_make_instr_get_element_ptr(proc, load, - index, NULL, 1, false); - element_type = t->pointer.element; - gep->instr.get_element_ptr.result_type = t->pointer.element; - gep->instr.get_element_ptr.element_type = t->pointer.element; - v = gep; - gb_printf("gep: %s\n", type_to_string(ssa_value_type(gep))); + v = ssa_emit_ptr_offset(proc, ptr, index); } break; case Type_Slice: { - GB_PANIC("ssa_build_addr AstNode_IndexExpression Type_Slice"); + ssaValue *slice = ssa_lvalue_address(ssa_build_addr(proc, ie->expr), proc); + ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); + ssaValue *elem = ssa_slice_elem(proc, slice); + v = ssa_emit_ptr_offset(proc, elem, index); } break; } - ssa_value_set_type(v, element_type); - return ssa_make_lvalue_address(ssa_emit(proc, v), expr); + // NOTE(bill): lvalue address encodes the pointer, thus the deref + ssa_value_set_type(v, type_deref(ssa_value_type(v))); + return ssa_make_lvalue_address(v, expr); case_end; case_ast_node(de, DerefExpr, expr); - // TODO(bill): Clean up - Type *t = type_of_expr(proc->module->info, de->expr); - t = type_deref(get_base_type(t)); - ssaValue *e = ssa_lvalue_address(ssa_build_addr(proc, de->expr), proc); - ssaValue *load = ssa_emit_load(proc, e); + ssaValue *load = ssa_emit_load(proc, ssa_lvalue_address(ssa_build_addr(proc, de->expr), proc)); ssaValue *gep = ssa_make_instr_get_element_ptr(proc, load, NULL, NULL, 0, false); + Type *t = ssa_value_type(load); + t = type_deref(get_base_type(t)); gep->instr.get_element_ptr.result_type = t; gep->instr.get_element_ptr.element_type = t; return ssa_make_lvalue_address(ssa_emit(proc, gep), expr); case_end; - - // TODO(bill): Others address } GB_PANIC("Unexpected address expression"); @@ -1134,8 +1288,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *s) { op.kind = Token_Sub; } ssaLvalue lval = ssa_build_addr(proc, ids->expr); - ssaValue *one = ssa_make_value_constant(proc->module->allocator, ssa_lvalue_type(lval), - make_exact_value_integer(1)); + ssaValue *one = ssa_emit_conv(proc, v_one, ssa_lvalue_type(lval)); ssa_build_assign_op(proc, lval, one, op); case_end; @@ -1323,8 +1476,6 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *s) { } } - - void ssa_build_proc(ssaValue *value) { ssaProcedure *proc = &value->proc;