From: Brendan Hansen Date: Sun, 20 Jun 2021 15:27:12 +0000 (-0500) Subject: added field accesses on r-values X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=b16ff88873fdbba5565109b25be271e16904b35f;p=onyx.git added field accesses on r-values --- diff --git a/bin/onyx b/bin/onyx index 125c1bc7..b1ce1672 100755 Binary files a/bin/onyx and b/bin/onyx differ diff --git a/include/onyxtypes.h b/include/onyxtypes.h index 97c5f0bd..f67bacec 100644 --- a/include/onyxtypes.h +++ b/include/onyxtypes.h @@ -184,6 +184,7 @@ b32 type_lookup_member_by_idx(Type* type, i32 idx, StructMember* smem); i32 type_linear_member_count(Type* type); b32 type_linear_member_lookup(Type* type, i32 idx, TypeWithOffset* two); +i32 type_get_idx_of_linear_member_with_offset(Type* type, u32 offset); b32 type_struct_is_simple(Type* type); diff --git a/src/onyxchecker.c b/src/onyxchecker.c index ee2c5395..0e27fde0 100644 --- a/src/onyxchecker.c +++ b/src/onyxchecker.c @@ -1378,15 +1378,7 @@ CheckStatus check_field_access(AstFieldAccess** pfield) { return Check_Error; } - if (field->expr->type->kind != Type_Kind_Pointer && !is_lval((AstNode *) field->expr)) { - onyx_report_error(field->token->pos, - "Cannot access field '%b'. Expression is not an lval.", - field->token->text, - field->token->length); - return Check_Error; - } - - // HACK: (*foo).bar does not work without this. + // Optimization for (*foo).member. if (field->expr->kind == Ast_Kind_Dereference) { field->expr = ((AstDereference *) field->expr)->expr; } diff --git a/src/onyxtypes.c b/src/onyxtypes.c index 0e307106..2cdccb79 100644 --- a/src/onyxtypes.c +++ b/src/onyxtypes.c @@ -902,7 +902,7 @@ i32 type_linear_member_count(Type* type) { case Type_Kind_DynArray: return 5; case Type_Kind_Compound: return bh_arr_length(type->Compound.linear_members); case Type_Kind_Struct: return bh_arr_length(type->Struct.linear_members); - default: return 0; + default: return 1; } } @@ -944,7 +944,50 @@ b32 type_linear_member_lookup(Type* type, i32 idx, TypeWithOffset* two) { } case Type_Kind_Compound: *two = type->Compound.linear_members[idx]; return 1; case Type_Kind_Struct: *two = type->Struct.linear_members[idx]; return 1; - default: return 0; + default: { + if (idx > 0) return 0; + two->offset = 0; + two->type = type; + return 1; + } + } +} + +i32 type_get_idx_of_linear_member_with_offset(Type* type, u32 offset) { + switch (type->kind) { + case Type_Kind_Slice: + case Type_Kind_VarArgs: { + if (offset == 0) return 0; + if (offset == 8) return 1; + return -1; + } + case Type_Kind_DynArray: { + if (offset == 0) return 0; + if (offset == 8) return 1; + if (offset == 12) return 2; + if (offset == 16) return 3; + if (offset == 24) return 4; + return -1; + } + case Type_Kind_Compound: { + i32 idx = 0; + bh_arr_each(TypeWithOffset, two, type->Compound.linear_members) { + if (two->offset == offset) return idx; + idx++; + } + + return -1; + } + case Type_Kind_Struct: { + i32 idx = 0; + bh_arr_each(TypeWithOffset, two, type->Struct.linear_members) { + if (two->offset == offset) return idx; + idx++; + } + + return -1; + } + default: return -1; } } diff --git a/src/onyxwasm.c b/src/onyxwasm.c index e2cc009b..0fa7eb94 100644 --- a/src/onyxwasm.c +++ b/src/onyxwasm.c @@ -1426,8 +1426,10 @@ EMIT_FUNC(call, AstCall* call) { switch (call->va_kind) { case VA_Kind_Typed: { WIL(WI_LOCAL_GET, stack_top_store_local); - WID(WI_PTR_CONST, (u32) vararg_offset); - WI(WI_PTR_ADD); + if (vararg_offset > 0) { + WID(WI_PTR_CONST, vararg_offset); + WI(WI_PTR_ADD); + } WID(WI_I32_CONST, vararg_count); break; } @@ -1435,8 +1437,10 @@ EMIT_FUNC(call, AstCall* call) { case VA_Kind_Untyped: { WIL(WI_LOCAL_GET, stack_top_store_local); WIL(WI_LOCAL_GET, stack_top_store_local); - WID(WI_PTR_CONST, vararg_offset); - WI(WI_PTR_ADD); + if (vararg_offset > 0) { + WID(WI_PTR_CONST, vararg_offset); + WI(WI_PTR_ADD); + } emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Rawptr], reserve_size); // NOTE: There will be 4 uninitialized bytes here, because pointers are only 4 bytes in WASM. @@ -1446,8 +1450,10 @@ EMIT_FUNC(call, AstCall* call) { emit_store_instruction(mod, &code, &basic_types[Basic_Kind_I32], reserve_size + 8); WIL(WI_LOCAL_GET, stack_top_store_local); - WID(WI_PTR_CONST, reserve_size); - WI(WI_PTR_ADD); + if (reserve_size > 0) { + WID(WI_PTR_CONST, reserve_size); + WI(WI_PTR_ADD); + } reserve_size += 12; break; @@ -2020,8 +2026,6 @@ EMIT_FUNC(compound_load, Type* type, u64 offset) { EMIT_FUNC(compound_store, Type* type, u64 offset, b32 location_first) { bh_arr(WasmInstruction) code = *pcode; - bh_arr(u64) temp_locals = NULL; - bh_arr_new(global_heap_allocator, temp_locals, 4); TypeWithOffset two; @@ -2029,14 +2033,14 @@ EMIT_FUNC(compound_store, Type* type, u64 offset, b32 location_first) { if (location_first) WIL(WI_LOCAL_SET, loc_idx); i32 elem_count = type_linear_member_count(type); + u64 *temp_locals = bh_alloc_array(global_scratch_allocator, u64, elem_count); + forir (i, elem_count - 1, 0) { type_linear_member_lookup(type, i, &two); WasmType wt = onyx_type_to_wasm_type(two.type); - u64 tmp_idx = local_raw_allocate(mod->local_alloc, wt); - - bh_arr_push(temp_locals, tmp_idx); - WIL(WI_LOCAL_SET, tmp_idx); + temp_locals[i] = local_raw_allocate(mod->local_alloc, wt); + WIL(WI_LOCAL_SET, temp_locals[i]); } if (!location_first) WIL(WI_LOCAL_SET, loc_idx); @@ -2044,7 +2048,7 @@ EMIT_FUNC(compound_store, Type* type, u64 offset, b32 location_first) { fori (i, 0, elem_count) { type_linear_member_lookup(type, i, &two); - u64 tmp_idx = bh_arr_pop(temp_locals); + u64 tmp_idx = temp_locals[i]; WIL(WI_LOCAL_GET, loc_idx); WIL(WI_LOCAL_GET, tmp_idx); emit_store_instruction(mod, &code, two.type, offset + two.offset); @@ -2053,9 +2057,11 @@ EMIT_FUNC(compound_store, Type* type, u64 offset, b32 location_first) { local_raw_free(mod->local_alloc, wt); } - bh_arr_free(temp_locals); local_raw_free(mod->local_alloc, WASM_TYPE_PTR); + // This shouldn't be necessary because the scratch allocator doesn't free. + bh_free(global_scratch_allocator, temp_locals); + *pcode = code; } @@ -2404,46 +2410,55 @@ EMIT_FUNC(expression, AstTyped* expr) { } } + if (is_lval((AstNode *) field->expr)) { + u64 offset = 0; + emit_field_access_location(mod, &code, field, &offset); + emit_load_instruction(mod, &code, field->type, offset); + + } else { + emit_expression(mod, &code, field->expr); - // HACK - // This is only used in the weird case where you write something like this: - // - // data_ptr := "Test string".data; - // string_count := "Some other string".count; - // - // I don't think this is every used in the core libraries or anything written in - // Onyx yet, so this might go away at some point. Actually, thinking about it, - // I think accessing fields on r-values is completely disabled currently, so this - // is never possible. - // - // I think a more general case of accessing fields on r-values is better. For example, - // - // returns_foo :: () -> Foo { ... } - // - // value := returns_foo().foo_member; - // - // This does not work in the language at the moment, but maybe should? - /* - else if (field->expr->kind == Ast_Kind_StrLit) { - StructMember smem; - - token_toggle_end(field->token); - type_lookup_member(field->expr->type, field->token->text, &smem); - token_toggle_end(field->token); - - if (smem.idx == 0) - WID(WI_I32_CONST, ((AstStrLit *) field->expr)->addr); - - if (smem.idx == 1) - WID(WI_I32_CONST, ((AstStrLit *) field->expr)->length); - - break; + i32 idx = type_get_idx_of_linear_member_with_offset(field->expr->type, field->offset); + i32 field_linear_members = type_linear_member_count(field->type); + i32 total_linear_members = type_linear_member_count(field->expr->type); + + if (idx == 0) { + // Easy case: the member is the first one and all other members just have to be dropped. + fori (i, 0, total_linear_members - field_linear_members) WI(WI_DROP); + + } else { + // Tough case: Stack shuffling to make the member the only thing on the stack. + // This is very similar to the compound_load/compound_store procedures but it is different enough + // that I cannot find a good way to factor them all without just introducing a ton of complexity. + fori (i, 0, total_linear_members - idx - field_linear_members) WI(WI_DROP); + + u64 *temporaries = bh_alloc_array(global_scratch_allocator, u64, field_linear_members); + fori (i, 0, field_linear_members) temporaries[i] = 0; + + TypeWithOffset two = { 0 }; + forir (i, field_linear_members - 1, 0) { + type_linear_member_lookup(field->type, i, &two); + + WasmType wt = onyx_type_to_wasm_type(two.type); + temporaries[i] = local_raw_allocate(mod->local_alloc, wt); + WIL(WI_LOCAL_SET, temporaries[i]); + } + + fori (i, 0, idx) WI(WI_DROP); + + fori (i, 0, field_linear_members) { + type_linear_member_lookup(field->type, i, &two); + + WIL(WI_LOCAL_GET, temporaries[i]); + + WasmType wt = onyx_type_to_wasm_type(two.type); + local_raw_free(mod->local_alloc, wt); + } + + bh_free(global_scratch_allocator, temporaries); + } } - */ - u64 offset = 0; - emit_field_access_location(mod, &code, field, &offset); - emit_load_instruction(mod, &code, field->type, offset); break; } diff --git a/tests/better_field_accesses b/tests/better_field_accesses new file mode 100644 index 00000000..c7378147 --- /dev/null +++ b/tests/better_field_accesses @@ -0,0 +1,5 @@ +0x1A2 +Foo bar +10 +42 +Worked! diff --git a/tests/better_field_accesses.onyx b/tests/better_field_accesses.onyx new file mode 100644 index 00000000..caba4d3b --- /dev/null +++ b/tests/better_field_accesses.onyx @@ -0,0 +1,27 @@ +#load "core/std" + +use package core + +Foo :: struct { + x, y: i32; + name: str; + age: i32; +} + +get_foo :: () -> Foo { + return .{ + x = 10, y = 20, + name = "Foo bar", + age = 42, + }; +} + +main :: (args: [] cstr) { + printf("%p\n", "Hello World!".data); + + println(get_foo().name); + println(get_foo().x); + println(get_foo().age); + + println((Foo.{ 0, 0, "Worked!", 0 }).name); +} \ No newline at end of file