From de49e904b603ec62fe2aae2c10badef9d1c0c7c5 Mon Sep 17 00:00:00 2001 From: Wodann Date: Thu, 7 May 2020 23:33:48 +0200 Subject: [PATCH 1/7] feat(codegen): generate unique GUID for struct(value) --- .../src/snapshots/test__field_expr.snap | 12 ++--- .../src/snapshots/test__nested_structs.snap | 44 +++++++++---------- .../src/snapshots/test__struct_test.snap | 2 +- crates/mun_codegen/src/type_info.rs | 3 +- crates/mun_hir/src/ty.rs | 28 +++++++++++- 5 files changed, 56 insertions(+), 33 deletions(-) diff --git a/crates/mun_codegen/src/snapshots/test__field_expr.snap b/crates/mun_codegen/src/snapshots/test__field_expr.snap index 2fac2fee9..e6b23cb08 100644 --- a/crates/mun_codegen/src/snapshots/test__field_expr.snap +++ b/crates/mun_codegen/src/snapshots/test__field_expr.snap @@ -76,18 +76,18 @@ source_filename = "group_name" @"type_info::<*const TypeInfo>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", [16 x i8]* @"type_info::<*const TypeInfo>::name", i32 64, i8 8, i8 0 } @"type_info::::name" = private unnamed_addr constant [10 x i8] c"core::f64\00" @"type_info::" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", [10 x i8]* @"type_info::::name", i32 64, i8 8, i8 0 } +@"type_info::<*const *mut core::void>::name" = private unnamed_addr constant [23 x i8] c"*const *mut core::void\00" +@"type_info::<*const *mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", [23 x i8]* @"type_info::<*const *mut core::void>::name", i32 64, i8 8, i8 0 } +@"type_info::<*mut core::void>::name" = private unnamed_addr constant [16 x i8] c"*mut core::void\00" +@"type_info::<*mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", [16 x i8]* @"type_info::<*mut core::void>::name", i32 64, i8 8, i8 0 } @"type_info::::name" = private unnamed_addr constant [4 x i8] c"Bar\00" @"struct_info::::field_names" = private unnamed_addr constant [2 x i8] c"0\00" @"struct_info::::field_names.1" = private unnamed_addr constant [2 x i8] c"1\00" @1 = private unnamed_addr constant [2 x i8 addrspace(4)*] [i8 addrspace(4)* @"struct_info::::field_names", i8 addrspace(4)* @"struct_info::::field_names.1"] @"struct_info::::field_types" = private unnamed_addr constant [2 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::"] @"struct_info::::field_offsets" = private unnamed_addr constant [2 x i16] [i16 0, i16 8] -@"type_info::" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"\89\18\04\82[\A4v\13u\DF\0A\C8Z9\F9\FA", [4 x i8]* @"type_info::::name", i32 128, i8 8, i8 1 }, %struct.MunStructInfo { [2 x i8 addrspace(4)*]* @1, [2 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [2 x i16]* @"struct_info::::field_offsets", i16 2, i8 1 } } -@"type_info::<*const *mut core::void>::name" = private unnamed_addr constant [23 x i8] c"*const *mut core::void\00" -@"type_info::<*const *mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", [23 x i8]* @"type_info::<*const *mut core::void>::name", i32 64, i8 8, i8 0 } -@"type_info::<*mut core::void>::name" = private unnamed_addr constant [16 x i8] c"*mut core::void\00" -@"type_info::<*mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", [16 x i8]* @"type_info::<*mut core::void>::name", i32 64, i8 8, i8 0 } -@global_type_table = global [7 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const TypeInfo>", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const *mut core::void>", %struct.MunTypeInfo addrspace(4)* @"type_info::<*mut core::void>"] +@"type_info::" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"\FC8#Lvd)F\B1Q\06\8B\02pl\10", [4 x i8]* @"type_info::::name", i32 128, i8 8, i8 1 }, %struct.MunStructInfo { [2 x i8 addrspace(4)*]* @1, [2 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [2 x i16]* @"struct_info::::field_offsets", i16 2, i8 1 } } +@global_type_table = global [7 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const TypeInfo>", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const *mut core::void>", %struct.MunTypeInfo addrspace(4)* @"type_info::<*mut core::void>", %struct.MunTypeInfo addrspace(4)* @"type_info::"] @allocatorHandle = unnamed_addr global i8* null declare i32 @foo_a(%Foo) diff --git a/crates/mun_codegen/src/snapshots/test__nested_structs.snap b/crates/mun_codegen/src/snapshots/test__nested_structs.snap index e0bda2aaf..ba2668fb8 100644 --- a/crates/mun_codegen/src/snapshots/test__nested_structs.snap +++ b/crates/mun_codegen/src/snapshots/test__nested_structs.snap @@ -43,7 +43,7 @@ define %ValueStruct* addrspace(4)* @new_value_struct_wrapper(float, float) { body: %new_value_struct = call %ValueStruct @new_value_struct(float %0, float %1) %new_ptr = load i8* addrspace(4)* (i8 addrspace(4)*, i8*)*, i8* addrspace(4)* (i8 addrspace(4)*, i8*)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0) - %ValueStruct_ptr = load %struct.MunTypeInfo addrspace(4)*, %struct.MunTypeInfo addrspace(4)** getelementptr inbounds ([8 x %struct.MunTypeInfo addrspace(4)*], [8 x %struct.MunTypeInfo addrspace(4)*]* @global_type_table, i32 0, i32 2) + %ValueStruct_ptr = load %struct.MunTypeInfo addrspace(4)*, %struct.MunTypeInfo addrspace(4)** getelementptr inbounds ([8 x %struct.MunTypeInfo addrspace(4)*], [8 x %struct.MunTypeInfo addrspace(4)*]* @global_type_table, i32 0, i32 4) %type_info_ptr_to_i8_ptr = bitcast %struct.MunTypeInfo addrspace(4)* %ValueStruct_ptr to i8 addrspace(4)* %allocator_handle = load i8*, i8** @allocatorHandle %new = call i8* addrspace(4)* %new_ptr(i8 addrspace(4)* %type_info_ptr_to_i8_ptr, i8* %allocator_handle) @@ -60,7 +60,7 @@ body: %init = insertvalue %GcWrapper undef, %GcStruct* addrspace(4)* %0, 0 %init3 = insertvalue %GcWrapper %init, %ValueStruct %1, 1 %new_ptr = load i8* addrspace(4)* (i8 addrspace(4)*, i8*)*, i8* addrspace(4)* (i8 addrspace(4)*, i8*)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0) - %GcWrapper_ptr = load %struct.MunTypeInfo addrspace(4)*, %struct.MunTypeInfo addrspace(4)** getelementptr inbounds ([8 x %struct.MunTypeInfo addrspace(4)*], [8 x %struct.MunTypeInfo addrspace(4)*]* @global_type_table, i32 0, i32 3) + %GcWrapper_ptr = load %struct.MunTypeInfo addrspace(4)*, %struct.MunTypeInfo addrspace(4)** getelementptr inbounds ([8 x %struct.MunTypeInfo addrspace(4)*], [8 x %struct.MunTypeInfo addrspace(4)*]* @global_type_table, i32 0, i32 0) %type_info_ptr_to_i8_ptr = bitcast %struct.MunTypeInfo addrspace(4)* %GcWrapper_ptr to i8 addrspace(4)* %allocator_handle = load i8*, i8** @allocatorHandle %new = call i8* addrspace(4)* %new_ptr(i8 addrspace(4)* %type_info_ptr_to_i8_ptr, i8* %allocator_handle) @@ -93,7 +93,7 @@ body: %deref = load %ValueStruct, %ValueStruct* %mem_ptr %new_value_wrapper = call %ValueWrapper @new_value_wrapper(%GcStruct* addrspace(4)* %0, %ValueStruct %deref) %new_ptr = load i8* addrspace(4)* (i8 addrspace(4)*, i8*)*, i8* addrspace(4)* (i8 addrspace(4)*, i8*)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0) - %ValueWrapper_ptr = load %struct.MunTypeInfo addrspace(4)*, %struct.MunTypeInfo addrspace(4)** getelementptr inbounds ([8 x %struct.MunTypeInfo addrspace(4)*], [8 x %struct.MunTypeInfo addrspace(4)*]* @global_type_table, i32 0, i32 4) + %ValueWrapper_ptr = load %struct.MunTypeInfo addrspace(4)*, %struct.MunTypeInfo addrspace(4)** getelementptr inbounds ([8 x %struct.MunTypeInfo addrspace(4)*], [8 x %struct.MunTypeInfo addrspace(4)*]* @global_type_table, i32 0, i32 2) %type_info_ptr_to_i8_ptr = bitcast %struct.MunTypeInfo addrspace(4)* %ValueWrapper_ptr to i8 addrspace(4)* %allocator_handle = load i8*, i8** @allocatorHandle %new = call i8* addrspace(4)* %new_ptr(i8 addrspace(4)* %type_info_ptr_to_i8_ptr, i8* %allocator_handle) @@ -113,42 +113,42 @@ source_filename = "group_name" %struct.MunStructInfo = type { i8 addrspace(4)* addrspace(4)*, %struct.MunTypeInfo addrspace(4)* addrspace(4)*, i16 addrspace(4)*, i16, i8 } @dispatchTable = global %DispatchTable zeroinitializer -@"type_info::<*const TypeInfo>::name" = private unnamed_addr constant [16 x i8] c"*const TypeInfo\00" -@"type_info::<*const TypeInfo>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", [16 x i8]* @"type_info::<*const TypeInfo>::name", i32 64, i8 8, i8 0 } -@"type_info::::name" = private unnamed_addr constant [10 x i8] c"core::f32\00" -@"type_info::" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", [10 x i8]* @"type_info::::name", i32 32, i8 4, i8 0 } -@"type_info::::name" = private unnamed_addr constant [12 x i8] c"ValueStruct\00" -@"struct_info::::field_names" = private unnamed_addr constant [2 x i8] c"0\00" -@"struct_info::::field_names.1" = private unnamed_addr constant [2 x i8] c"1\00" -@0 = private unnamed_addr constant [2 x i8 addrspace(4)*] [i8 addrspace(4)* @"struct_info::::field_names", i8 addrspace(4)* @"struct_info::::field_names.1"] -@"struct_info::::field_types" = private unnamed_addr constant [2 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::"] -@"struct_info::::field_offsets" = private unnamed_addr constant [2 x i16] [i16 0, i16 4] -@"type_info::" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"U0{\87\5C\04Q/\95!$\A2\F1\A9\F9W", [12 x i8]* @"type_info::::name", i32 64, i8 4, i8 1 }, %struct.MunStructInfo { [2 x i8 addrspace(4)*]* @0, [2 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [2 x i16]* @"struct_info::::field_offsets", i16 2, i8 1 } } @"type_info::::name" = private unnamed_addr constant [10 x i8] c"GcWrapper\00" @"struct_info::::field_names" = private unnamed_addr constant [2 x i8] c"0\00" -@"struct_info::::field_names.2" = private unnamed_addr constant [2 x i8] c"1\00" -@1 = private unnamed_addr constant [2 x i8 addrspace(4)*] [i8 addrspace(4)* @"struct_info::::field_names", i8 addrspace(4)* @"struct_info::::field_names.2"] +@"struct_info::::field_names.1" = private unnamed_addr constant [2 x i8] c"1\00" +@0 = private unnamed_addr constant [2 x i8 addrspace(4)*] [i8 addrspace(4)* @"struct_info::::field_names", i8 addrspace(4)* @"struct_info::::field_names.1"] @"type_info::::name" = private unnamed_addr constant [9 x i8] c"GcStruct\00" @"struct_info::::field_names" = private unnamed_addr constant [2 x i8] c"0\00" -@"struct_info::::field_names.3" = private unnamed_addr constant [2 x i8] c"1\00" -@2 = private unnamed_addr constant [2 x i8 addrspace(4)*] [i8 addrspace(4)* @"struct_info::::field_names", i8 addrspace(4)* @"struct_info::::field_names.3"] +@"struct_info::::field_names.2" = private unnamed_addr constant [2 x i8] c"1\00" +@1 = private unnamed_addr constant [2 x i8 addrspace(4)*] [i8 addrspace(4)* @"struct_info::::field_names", i8 addrspace(4)* @"struct_info::::field_names.2"] +@"type_info::::name" = private unnamed_addr constant [10 x i8] c"core::f32\00" +@"type_info::" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", [10 x i8]* @"type_info::::name", i32 32, i8 4, i8 0 } @"struct_info::::field_types" = private unnamed_addr constant [2 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::"] @"struct_info::::field_offsets" = private unnamed_addr constant [2 x i16] [i16 0, i16 4] -@"type_info::" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"\B9)lg\01\95k@E\B4(\CB\CAGX\E1", [9 x i8]* @"type_info::::name", i32 64, i8 4, i8 1 }, %struct.MunStructInfo { [2 x i8 addrspace(4)*]* @2, [2 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [2 x i16]* @"struct_info::::field_offsets", i16 2, i8 0 } } +@"type_info::" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"\B9)lg\01\95k@E\B4(\CB\CAGX\E1", [9 x i8]* @"type_info::::name", i32 64, i8 4, i8 1 }, %struct.MunStructInfo { [2 x i8 addrspace(4)*]* @1, [2 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [2 x i16]* @"struct_info::::field_offsets", i16 2, i8 0 } } +@"type_info::::name" = private unnamed_addr constant [12 x i8] c"ValueStruct\00" +@"struct_info::::field_names" = private unnamed_addr constant [2 x i8] c"0\00" +@"struct_info::::field_names.3" = private unnamed_addr constant [2 x i8] c"1\00" +@2 = private unnamed_addr constant [2 x i8 addrspace(4)*] [i8 addrspace(4)* @"struct_info::::field_names", i8 addrspace(4)* @"struct_info::::field_names.3"] +@"struct_info::::field_types" = private unnamed_addr constant [2 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::"] +@"struct_info::::field_offsets" = private unnamed_addr constant [2 x i16] [i16 0, i16 4] +@"type_info::" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"U0{\87\5C\04Q/\95!$\A2\F1\A9\F9W", [12 x i8]* @"type_info::::name", i32 64, i8 4, i8 1 }, %struct.MunStructInfo { [2 x i8 addrspace(4)*]* @2, [2 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [2 x i16]* @"struct_info::::field_offsets", i16 2, i8 1 } } @"struct_info::::field_types" = private unnamed_addr constant [2 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::"] @"struct_info::::field_offsets" = private unnamed_addr constant [2 x i16] [i16 0, i16 8] -@"type_info::" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"cR\F04\B9\E3&)\14|\B77\C4jQ\D5", [10 x i8]* @"type_info::::name", i32 128, i8 8, i8 1 }, %struct.MunStructInfo { [2 x i8 addrspace(4)*]* @1, [2 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [2 x i16]* @"struct_info::::field_offsets", i16 2, i8 0 } } +@"type_info::" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"!\14\93\A7H1?90\B7\EA\DB0\82\A0\C7", [10 x i8]* @"type_info::::name", i32 128, i8 8, i8 1 }, %struct.MunStructInfo { [2 x i8 addrspace(4)*]* @0, [2 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [2 x i16]* @"struct_info::::field_offsets", i16 2, i8 0 } } +@"type_info::<*const TypeInfo>::name" = private unnamed_addr constant [16 x i8] c"*const TypeInfo\00" +@"type_info::<*const TypeInfo>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", [16 x i8]* @"type_info::<*const TypeInfo>::name", i32 64, i8 8, i8 0 } @"type_info::::name" = private unnamed_addr constant [13 x i8] c"ValueWrapper\00" @"struct_info::::field_names" = private unnamed_addr constant [2 x i8] c"0\00" @"struct_info::::field_names.4" = private unnamed_addr constant [2 x i8] c"1\00" @3 = private unnamed_addr constant [2 x i8 addrspace(4)*] [i8 addrspace(4)* @"struct_info::::field_names", i8 addrspace(4)* @"struct_info::::field_names.4"] @"struct_info::::field_types" = private unnamed_addr constant [2 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::"] @"struct_info::::field_offsets" = private unnamed_addr constant [2 x i16] [i16 0, i16 8] -@"type_info::" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"\99\1A\7F\CB\B36\A4\8Br\07\FB\7F\F0\86S\9F", [13 x i8]* @"type_info::::name", i32 128, i8 8, i8 1 }, %struct.MunStructInfo { [2 x i8 addrspace(4)*]* @3, [2 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [2 x i16]* @"struct_info::::field_offsets", i16 2, i8 1 } } +@"type_info::" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"@j\D8\CD~-\12\87|A\E8\DBp\EC}\AA", [13 x i8]* @"type_info::::name", i32 128, i8 8, i8 1 }, %struct.MunStructInfo { [2 x i8 addrspace(4)*]* @3, [2 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [2 x i16]* @"struct_info::::field_offsets", i16 2, i8 1 } } @"type_info::<*const *mut core::void>::name" = private unnamed_addr constant [23 x i8] c"*const *mut core::void\00" @"type_info::<*const *mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", [23 x i8]* @"type_info::<*const *mut core::void>::name", i32 64, i8 8, i8 0 } @"type_info::<*mut core::void>::name" = private unnamed_addr constant [16 x i8] c"*mut core::void\00" @"type_info::<*mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", [16 x i8]* @"type_info::<*mut core::void>::name", i32 64, i8 8, i8 0 } -@global_type_table = global [8 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::<*const TypeInfo>", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const *mut core::void>", %struct.MunTypeInfo addrspace(4)* @"type_info::<*mut core::void>"] +@global_type_table = global [8 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const TypeInfo>", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const *mut core::void>", %struct.MunTypeInfo addrspace(4)* @"type_info::<*mut core::void>"] @allocatorHandle = unnamed_addr global i8* null diff --git a/crates/mun_codegen/src/snapshots/test__struct_test.snap b/crates/mun_codegen/src/snapshots/test__struct_test.snap index 35e5449f4..d43953d09 100644 --- a/crates/mun_codegen/src/snapshots/test__struct_test.snap +++ b/crates/mun_codegen/src/snapshots/test__struct_test.snap @@ -69,7 +69,7 @@ source_filename = "group_name" @1 = private unnamed_addr constant [4 x i8 addrspace(4)*] [i8 addrspace(4)* @"struct_info::::field_names", i8 addrspace(4)* @"struct_info::::field_names.1", i8 addrspace(4)* @"struct_info::::field_names.2", i8 addrspace(4)* @"struct_info::::field_names.3"] @"struct_info::::field_types" = private unnamed_addr constant [4 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::"] @"struct_info::::field_offsets" = private unnamed_addr constant [4 x i16] [i16 0, i16 8, i16 12, i16 16] -@"type_info::" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"\EE\E7\C4\9B\81\B2EGXc\B0O\C8\C4\8C\89", [4 x i8]* @"type_info::::name", i32 192, i8 8, i8 1 }, %struct.MunStructInfo { [4 x i8 addrspace(4)*]* @1, [4 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [4 x i16]* @"struct_info::::field_offsets", i16 4, i8 1 } } +@"type_info::" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"\D6\CA\E2\C3Ht\09\EA\AEh\E50L\F7\EE\B5", [4 x i8]* @"type_info::::name", i32 192, i8 8, i8 1 }, %struct.MunStructInfo { [4 x i8 addrspace(4)*]* @1, [4 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [4 x i16]* @"struct_info::::field_offsets", i16 4, i8 1 } } @"type_info::<*mut core::void>::name" = private unnamed_addr constant [16 x i8] c"*mut core::void\00" @"type_info::<*mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", [16 x i8]* @"type_info::<*mut core::void>::name", i32 64, i8 8, i8 0 } @global_type_table = global [9 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const TypeInfo>", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const *mut core::void>", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*mut core::void>"] diff --git a/crates/mun_codegen/src/type_info.rs b/crates/mun_codegen/src/type_info.rs index d4d5790e1..125d7d6a4 100644 --- a/crates/mun_codegen/src/type_info.rs +++ b/crates/mun_codegen/src/type_info.rs @@ -100,13 +100,12 @@ impl TypeInfo { .map(|f| { let ty_string = f .ty(db) - .name_to_string(db) + .guid_string(db) .expect("type should be convertible to a string"); format!("{}: {}", f.name(db).to_string(), ty_string) }) .collect(); - // TODO: struct(value) vs struct(gc) format!( "struct {name}{{{fields}}}", name = &name, diff --git a/crates/mun_hir/src/ty.rs b/crates/mun_hir/src/ty.rs index dfc545c0f..1362662a9 100644 --- a/crates/mun_hir/src/ty.rs +++ b/crates/mun_hir/src/ty.rs @@ -144,9 +144,33 @@ impl Ty { /// Returns the type's name as a string, if one exists. /// /// This name needs to be unique as it is used to generate a type's `Guid`. - pub fn name_to_string(&self, db: &impl HirDatabase) -> Option { + pub fn guid_string(&self, db: &impl HirDatabase) -> Option { self.as_simple().and_then(|ty_ctor| match ty_ctor { - TypeCtor::Struct(s) => Some(format!("struct {}", s.name(db).to_string())), + TypeCtor::Struct(s) => { + let name = s.name(db).to_string(); + + Some(if s.data(db).memory_kind == StructMemoryKind::GC { + format!("struct {}", name) + } else { + let fields: Vec = s + .fields(db) + .into_iter() + .map(|f| { + let ty_string = f + .ty(db) + .guid_string(db) + .expect("type should be convertible to a string"); + format!("{}: {}", f.name(db).to_string(), ty_string) + }) + .collect(); + + format!( + "struct {name}{{{fields}}}", + name = name, + fields = fields.join(",") + ) + }) + } TypeCtor::Bool => Some("core::bool".to_string()), TypeCtor::Float(ty) => Some(format!("core::{}", ty.as_str())), TypeCtor::Int(ty) => Some(format!("core::{}", ty.as_str())), From 1429d1a966f3bd961fe1f8e83c8a68e7a1b589c6 Mon Sep 17 00:00:00 2001 From: Wodann Date: Thu, 7 May 2020 23:39:32 +0200 Subject: [PATCH 2/7] fix(memory): incorrect indexing in diff calculation --- crates/mun_memory/src/diff.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/mun_memory/src/diff.rs b/crates/mun_memory/src/diff.rs index 7bbc58898..97d848998 100644 --- a/crates/mun_memory/src/diff.rs +++ b/crates/mun_memory/src/diff.rs @@ -220,10 +220,10 @@ fn append_struct_mapping( break; } - let delete_idx = idx / num_inserted; + let delete_idx = idx % num_deleted; unsafe { *used_deletions.get_unchecked_mut(delete_idx) = true }; - let insert_idx = idx % num_inserted; + let insert_idx = idx / num_deleted; unsafe { *used_insertions.get_unchecked_mut(insert_idx) = true }; let old_index = unsafe { *deletions.get_unchecked(delete_idx) }; @@ -255,16 +255,14 @@ fn append_struct_mapping( // Prevent the row corresponding to the insertion entry from being used again, by inserting // `std::usize::MAX`. for idx in 0..num_deleted { - let offset = insert_idx * num_deleted; - let idx = offset + idx; + let idx = insert_idx * num_deleted + idx; unsafe { *myers_lengths.get_unchecked_mut(idx) = std::usize::MAX }; } // Prevent the column corresponding to the deletion entry from being used again, by // inserting `std::usize::MAX`. for idx in 0..num_inserted { - let offset = delete_idx; - let idx = idx * num_deleted + offset; + let idx = idx * num_deleted + delete_idx; unsafe { *myers_lengths.get_unchecked_mut(idx) = std::usize::MAX }; } } From ba955d6caf4b1ce649f28de30834a8828413e3de Mon Sep 17 00:00:00 2001 From: Wodann Date: Fri, 8 May 2020 18:07:43 +0200 Subject: [PATCH 3/7] feat(memory): add memory kind to TypeLayout trait as memory mapping requires knowledge about a struct's memory kind, this needed to be added to one of the memory::Type{..} traits. TypeLayout seemed most applicable --- crates/mun_memory/src/lib.rs | 2 ++ crates/mun_memory/tests/diff/util.rs | 5 +++++ crates/mun_memory/tests/gc/util.rs | 5 +++++ crates/mun_runtime/src/garbage_collector.rs | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/crates/mun_memory/src/lib.rs b/crates/mun_memory/src/lib.rs index 1d99d82c5..8f32b34ad 100644 --- a/crates/mun_memory/src/lib.rs +++ b/crates/mun_memory/src/lib.rs @@ -24,6 +24,8 @@ pub trait TypeDesc: Send + Sync { pub trait TypeLayout: Send + Sync { /// Returns the memory layout of this type. fn layout(&self) -> Layout; + /// Returns the memory kind of this type, if it is a struct. + fn memory_kind(&self) -> Option; } /// A trait used to obtain a type's fields. diff --git a/crates/mun_memory/tests/diff/util.rs b/crates/mun_memory/tests/diff/util.rs index 7c9375eb3..06743107c 100644 --- a/crates/mun_memory/tests/diff/util.rs +++ b/crates/mun_memory/tests/diff/util.rs @@ -110,6 +110,11 @@ impl TypeLayout for &TypeInfo { fn layout(&self) -> Layout { self.layout } + + fn memory_kind(&self) -> Option { + // NOTE: This contrived test does not support structs + None + } } impl<'t> TypeFields<&'t TypeInfo> for &'t TypeInfo { diff --git a/crates/mun_memory/tests/gc/util.rs b/crates/mun_memory/tests/gc/util.rs index e8b0a3f75..065ae7d82 100644 --- a/crates/mun_memory/tests/gc/util.rs +++ b/crates/mun_memory/tests/gc/util.rs @@ -77,6 +77,11 @@ impl mun_memory::TypeLayout for &'static TypeInfo { Layout::from_size_align(self.size as usize, self.alignment as usize) .expect("invalid layout specified by TypeInfo") } + + fn memory_kind(&self) -> Option { + // NOTE: This contrived test does not support structs + None + } } impl gc::TypeTrace for &'static TypeInfo { diff --git a/crates/mun_runtime/src/garbage_collector.rs b/crates/mun_runtime/src/garbage_collector.rs index d439c96b3..42f8af634 100644 --- a/crates/mun_runtime/src/garbage_collector.rs +++ b/crates/mun_runtime/src/garbage_collector.rs @@ -119,6 +119,10 @@ impl memory::TypeLayout for UnsafeTypeInfo { Layout::from_size_align(ty.size_in_bytes(), ty.alignment()) .unwrap_or_else(|_| panic!("invalid layout from Mun Type: {:?}", ty)) } + + fn memory_kind(&self) -> Option { + unsafe { self.0.as_ref().as_struct().map(|s| s.memory_kind.clone()) } + } } impl gc::TypeTrace for UnsafeTypeInfo { From 082b9cb8ca698224a9ce5a8468e569a52147c099 Mon Sep 17 00:00:00 2001 From: Wodann Date: Fri, 8 May 2020 18:11:21 +0200 Subject: [PATCH 4/7] refactor(memory): split mark&sweep alloc function --- crates/mun_memory/src/gc/mark_sweep.rs | 36 ++++++++++++++++---------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/crates/mun_memory/src/gc/mark_sweep.rs b/crates/mun_memory/src/gc/mark_sweep.rs index bda6fefb8..564f490c2 100644 --- a/crates/mun_memory/src/gc/mark_sweep.rs +++ b/crates/mun_memory/src/gc/mark_sweep.rs @@ -54,26 +54,39 @@ where } } + /// Logs an allocation + fn log_alloc(&self, handle: GcPtr, ty: T) { + { + let mut stats = self.stats.write(); + stats.allocated_memory += ty.layout().size(); + } + + self.observer.event(Event::Allocation(handle)); + } + /// Returns the observer pub fn observer(&self) -> &O { &self.observer } } +fn alloc_obj(ty: T) -> Pin>> { + let ptr = unsafe { std::alloc::alloc(ty.layout()) }; + Box::pin(ObjectInfo { + ptr, + ty, + roots: 0, + color: Color::White, + }) +} + impl GcRuntime for MarkSweep where T: TypeLayout + TypeTrace + Clone, O: Observer, { fn alloc(&self, ty: T) -> GcPtr { - let layout = ty.layout(); - let ptr = unsafe { std::alloc::alloc(layout) }; - let object = Box::pin(ObjectInfo { - ptr, - ty, - roots: 0, - color: Color::White, - }); + let object = alloc_obj(ty.clone()); // We want to return a pointer to the `ObjectInfo`, to be used as handle. let handle = (object.as_ref().deref() as *const _ as RawGcPtr).into(); @@ -83,12 +96,7 @@ where objects.insert(handle, object); } - { - let mut stats = self.stats.write(); - stats.allocated_memory += layout.size(); - } - - self.observer.event(Event::Allocation(handle)); + self.log_alloc(handle, ty); handle } From 890b06b453410940863ba5ee127749bfa36ed3b6 Mon Sep 17 00:00:00 2001 From: Wodann Date: Fri, 8 May 2020 18:14:53 +0200 Subject: [PATCH 5/7] feat(memory): map structs with different memory kinds this also fixes "identity" memory mapping, where only an old type pointer needed to be replaced with a new pointer to the same type information --- crates/mun_memory/src/gc/mark_sweep.rs | 240 ++++++++++++++++++++++--- crates/mun_memory/src/mapping.rs | 87 +++++++-- crates/mun_runtime/tests/memory.rs | 204 +++++++++++++++++++++ 3 files changed, 491 insertions(+), 40 deletions(-) diff --git a/crates/mun_memory/src/gc/mark_sweep.rs b/crates/mun_memory/src/gc/mark_sweep.rs index 564f490c2..abd475133 100644 --- a/crates/mun_memory/src/gc/mark_sweep.rs +++ b/crates/mun_memory/src/gc/mark_sweep.rs @@ -2,7 +2,7 @@ use crate::{ cast, gc::{Event, GcPtr, GcRuntime, Observer, RawGcPtr, Stats, TypeTrace}, mapping::{self, FieldMapping, MemoryMapper}, - TypeLayout, + TypeDesc, TypeLayout, }; use mapping::{Conversion, Mapping}; use parking_lot::RwLock; @@ -207,7 +207,7 @@ where impl MemoryMapper for MarkSweep where - T: TypeLayout + TypeTrace + Clone + Eq + Hash, + T: TypeDesc + TypeLayout + TypeTrace + Clone + Eq + Hash, O: Observer, { fn map_memory(&self, mapping: Mapping) -> Vec { @@ -225,62 +225,248 @@ where }) .collect(); - for (old_ty, conversion) in mapping.conversions { + // Update type pointers of types that didn't change + for (old_ty, new_ty) in mapping.identical { for object_info in objects.values_mut() { if object_info.ty == old_ty { - map_fields(object_info, &conversion); + object_info.set(ObjectInfo { + ptr: object_info.ptr, + roots: object_info.roots, + color: object_info.color, + ty: new_ty.clone(), + }); } } } - return deleted; + let mut new_allocations = Vec::new(); + + for (old_ty, conversion) in mapping.conversions.iter() { + for object_info in objects.values_mut() { + if object_info.ty == *old_ty { + let src = unsafe { NonNull::new_unchecked(object_info.ptr) }; + let dest = unsafe { + NonNull::new_unchecked(std::alloc::alloc_zeroed(conversion.new_ty.layout())) + }; + + map_fields( + self, + &mut new_allocations, + &mapping.conversions, + &conversion.field_mapping, + src, + dest, + ); + + unsafe { std::alloc::dealloc(src.as_ptr(), old_ty.layout()) }; + + object_info.set(ObjectInfo { + ptr: dest.as_ptr(), + roots: object_info.roots, + color: object_info.color, + ty: conversion.new_ty.clone(), + }); + } + } + } + + // Retroactively store newly allocated objects + // This cannot be done while mapping because we hold a mutable reference to objects + for object in new_allocations { + let ty = object.ty.clone(); + // We want to return a pointer to the `ObjectInfo`, to + // be used as handle. + let handle = (object.as_ref().deref() as *const _ as RawGcPtr).into(); + objects.insert(handle, object); - fn map_fields( - object_info: &mut Pin>>, - conversion: &Conversion, - ) { - let ptr = unsafe { std::alloc::alloc_zeroed(conversion.new_ty.layout()) }; + self.log_alloc(handle, ty); + } - for map in conversion.field_mapping.iter() { + return deleted; + + fn map_fields( + gc: &MarkSweep, + new_allocations: &mut Vec>>>, + conversions: &HashMap>, + mapping: &[Option>], + src: NonNull, + dest: NonNull, + ) where + T: TypeDesc + TypeLayout + TypeTrace + Clone + Eq + Hash, + O: Observer, + { + for map in mapping.iter() { if let Some(FieldMapping { old_offset, new_offset, action, }) = map { - let src = { - let mut src = object_info.ptr as usize; + let field_src = { + let mut src = src.as_ptr() as usize; src += old_offset; src as *mut u8 }; - let dest = { - let mut dest = ptr as usize; + let field_dest = { + let mut dest = dest.as_ptr() as usize; dest += new_offset; dest as *mut u8 }; match action { mapping::Action::Cast { old, new } => { - if !cast::try_cast_from_to( - *old, - *new, - unsafe { NonNull::new_unchecked(src) }, - unsafe { NonNull::new_unchecked(dest) }, + if let Some(old_memory_kind) = old.memory_kind() { + let new_memory_kind = new.memory_kind().expect( + "We are dealing with a struct and we do not directly compare fundamental types and struct type, so its counterpart must also be a struct.", + ); + + // When the name is the same, we are dealing with the same struct, + // but different internals + let is_same_struct = old.name() == new.name(); + + // If the same struct changed, there must also be a conversion + let conversion = conversions.get(old); + + if old_memory_kind == abi::StructMemoryKind::Value { + if new_memory_kind == abi::StructMemoryKind::Value { + // struct(value) -> struct(value) + if is_same_struct { + // Map in-memory struct to in-memory struct + map_fields( + gc, + new_allocations, + conversions, + &conversion.as_ref().unwrap().field_mapping, + unsafe { NonNull::new_unchecked(field_src) }, + unsafe { NonNull::new_unchecked(field_dest) }, + ); + } else { + // Use previously zero-initialized memory + } + } else { + // struct(value) -> struct(gc) + let object = alloc_obj(new.clone()); + + // We want to return a pointer to the `ObjectInfo`, to be used as handle. + let handle = (object.as_ref().deref() as *const _ + as RawGcPtr) + .into(); + + if is_same_struct { + // Map in-memory struct to heap-allocated struct + map_fields( + gc, + new_allocations, + conversions, + &conversion.as_ref().unwrap().field_mapping, + unsafe { NonNull::new_unchecked(field_src) }, + unsafe { NonNull::new_unchecked(object.ptr) }, + ); + } else { + // Zero initialize heap-allocated object + unsafe { + std::ptr::write_bytes( + (*object).ptr, + 0, + new.layout().size(), + ) + }; + } + + // Write handle to field + let field_handle = field_dest.cast::(); + unsafe { *field_handle = handle }; + + new_allocations.push(object); + } + } else if new_memory_kind == abi::StructMemoryKind::GC { + // struct(gc) -> struct(gc) + let field_src = field_src.cast::(); + let field_dest = field_dest.cast::(); + + if is_same_struct { + // Only copy the `GcPtr`. Memory will already be mapped. + unsafe { + *field_dest = *field_src; + } + } else { + let object = alloc_obj(new.clone()); + + // We want to return a pointer to the `ObjectInfo`, to + // be used as handle. + let handle = (object.as_ref().deref() as *const _ + as RawGcPtr) + .into(); + + // Zero-initialize heap-allocated object + unsafe { + std::ptr::write_bytes( + object.ptr, + 0, + new.layout().size(), + ) + }; + + // Write handle to field + unsafe { + *field_dest = handle; + } + + new_allocations.push(object); + } + } else { + // struct(gc) -> struct(value) + let field_handle = unsafe { *field_src.cast::() }; + + // Convert the handle to our internal representation + // Safety: we already hold a write lock on `objects`, so + // this is legal. + let obj: *mut ObjectInfo = field_handle.into(); + let obj = unsafe { &*obj }; + + if is_same_struct { + if obj.ty == *old { + // The object still needs to be mapped + // Map heap-allocated struct to in-memory struct + map_fields( + gc, + new_allocations, + conversions, + &conversion.as_ref().unwrap().field_mapping, + unsafe { NonNull::new_unchecked(obj.ptr) }, + unsafe { NonNull::new_unchecked(field_dest) }, + ); + } else { + // The object was already mapped + debug_assert!(obj.ty == *new); + + // Copy from heap-allocated struct to in-memory struct + unsafe { + std::ptr::copy_nonoverlapping( + obj.ptr, + field_dest, + obj.ty.layout().size(), + ) + }; + } + } else { + // Use previously zero-initialized memory + } + } + } else if !cast::try_cast_from_to( + *old.guid(), + *new.guid(), + unsafe { NonNull::new_unchecked(field_src) }, + unsafe { NonNull::new_unchecked(field_dest) }, ) { // Failed to cast. Use the previously zero-initialized value instead } } mapping::Action::Copy { size } => unsafe { - std::ptr::copy_nonoverlapping(src, dest, *size) + std::ptr::copy_nonoverlapping(field_src, field_dest, *size); }, } } } - object_info.set(ObjectInfo { - ptr, - roots: object_info.roots, - color: object_info.color, - ty: conversion.new_ty.clone(), - }); } } } diff --git a/crates/mun_memory/src/mapping.rs b/crates/mun_memory/src/mapping.rs index f4573f76e..b994a4319 100644 --- a/crates/mun_memory/src/mapping.rs +++ b/crates/mun_memory/src/mapping.rs @@ -8,28 +8,29 @@ use std::{ hash::Hash, }; -pub struct Mapping { +pub struct Mapping { pub deletions: HashSet, pub conversions: HashMap>, + pub identical: Vec<(T, T)>, } -pub struct Conversion { - pub field_mapping: Vec>, +pub struct Conversion { + pub field_mapping: Vec>>, pub new_ty: T, } /// Description of the mapping of a single field. When stored together with the new index, this /// provides all information necessary for a mapping function. -pub struct FieldMapping { +pub struct FieldMapping { pub old_offset: usize, pub new_offset: usize, - pub action: Action, + pub action: Action, } /// The `Action` to take when mapping memory from A to B. #[derive(Eq, PartialEq)] -pub enum Action { - Cast { old: abi::Guid, new: abi::Guid }, +pub enum Action { + Cast { old: T, new: T }, Copy { size: usize }, } @@ -41,8 +42,11 @@ where pub fn new(old: &[T], new: &[T]) -> Self { let diff = diff(old, new); - let mut deletions = HashSet::new(); let mut conversions = HashMap::new(); + let mut deletions = HashSet::new(); + let mut insertions = HashSet::new(); + + let mut identical = Vec::new(); for diff in diff.iter() { match diff { @@ -58,13 +62,70 @@ where let new_ty = unsafe { *new.get_unchecked(*new_index) }; conversions.insert(old_ty, unsafe { field_mapping(old_ty, new_ty, diff) }); } - Diff::Insert { .. } | Diff::Move { .. } => (), + Diff::Insert { index } => { + insertions.insert(unsafe { *new.get_unchecked(*index) }); + } + Diff::Move { + old_index, + new_index, + } => identical.push(unsafe { + ( + *old.get_unchecked(*old_index), + *new.get_unchecked(*new_index), + ) + }), } } + // These candidates are used to collect a list of `new_index -> old_index` mappings for + // identical types. + let mut new_candidates: HashSet = new + .iter() + // Filter non-struct types + .filter(|ty| ty.memory_kind().is_some()) + // Filter inserted structs + .filter(|ty| !insertions.contains(*ty)) + .cloned() + .collect(); + + let mut old_candidates: HashSet = old + .iter() + // Filter non-struct types + .filter(|ty| ty.memory_kind().is_some()) + // Filter deleted structs + .filter(|ty| !deletions.contains(*ty)) + // Filter edited types + .filter(|ty| { + if let Some(conversion) = conversions.get(*ty) { + // Remove its new counterpart too + new_candidates.remove(&conversion.new_ty); + false + } else { + true + } + }) + .cloned() + .collect(); + + // Remove moved types from the candidates, since we already know they are identical + for (old_ty, new_ty) in identical.iter() { + old_candidates.remove(old_ty); + new_candidates.remove(new_ty); + } + + // Find matching (old_ty, new_ty) pairs + for old_ty in old_candidates { + let new_ty = new_candidates.take(&old_ty).unwrap(); + identical.push((old_ty, new_ty)); + } + + // We should have matched all remaining candidates + debug_assert!(new_candidates.is_empty()); + Self { deletions, conversions, + identical, } } } @@ -77,7 +138,7 @@ where /// # Safety /// /// Expects the `diff` to be based on `old_ty` and `new_ty`. If not, it causes undefined behavior. -pub unsafe fn field_mapping + TypeLayout>( +pub unsafe fn field_mapping + TypeLayout>( old_ty: T, new_ty: T, diff: &[FieldDiff], @@ -179,8 +240,8 @@ pub unsafe fn field_mapping + TypeLayout>( new_offset: usize::from(*new_offsets.get_unchecked(new_index)), action: if desc.action == ActionDesc::Cast { Action::Cast { - old: *old_field.1.guid(), - new: *new_fields.get_unchecked(new_index).1.guid(), + old: old_field.1.clone(), + new: new_fields.get_unchecked(new_index).1.clone(), } } else { Action::Copy { @@ -196,7 +257,7 @@ pub unsafe fn field_mapping + TypeLayout>( } /// A trait used to map allocated memory using type differences. -pub trait MemoryMapper { +pub trait MemoryMapper { /// Maps its allocated memory using the provided `mapping`. /// /// A `Vec` is returned containing all objects of types that were deleted. The diff --git a/crates/mun_runtime/tests/memory.rs b/crates/mun_runtime/tests/memory.rs index 82c94e645..736a8d52c 100644 --- a/crates/mun_runtime/tests/memory.rs +++ b/crates/mun_runtime/tests/memory.rs @@ -532,3 +532,207 @@ fn delete_used_struct() { assert_eq!(foo.get::("b").unwrap(), b); assert_eq!(foo.get::("c").unwrap(), c); } + +#[test] +fn nested_structs() { + let mut driver = TestDriver::new( + r#" + struct(gc) GcStruct(f32, f32); + struct(value) ValueStruct(f32, f32); + + struct(gc) GcWrapper(GcStruct, ValueStruct) + struct(value) ValueWrapper(GcStruct, ValueStruct); + + pub fn new_gc_struct(a: f32, b: f32) -> GcStruct { + GcStruct(a, b) + } + + pub fn new_value_struct(a: f32, b: f32) -> ValueStruct { + ValueStruct(a, b) + } + + pub fn new_gc_wrapper(a: GcStruct, b: ValueStruct) -> GcWrapper { + GcWrapper(a, b) + } + + pub fn new_value_wrapper(a: GcStruct, b: ValueStruct) -> ValueWrapper { + ValueWrapper(a, b) + } + "#, + ); + + let a = -3.14f32; + let b = 6.18f32; + let gc_struct: StructRef = invoke_fn!(driver.runtime_mut(), "new_gc_struct", a, b).unwrap(); + let value_struct: StructRef = + invoke_fn!(driver.runtime_mut(), "new_value_struct", a, b).unwrap(); + + let gc_wrapper: StructRef = invoke_fn!( + driver.runtime_mut(), + "new_gc_wrapper", + gc_struct.clone(), + value_struct.clone() + ) + .unwrap(); + + let value_wrapper: StructRef = invoke_fn!( + driver.runtime_mut(), + "new_value_wrapper", + gc_struct.clone(), + value_struct.clone() + ) + .unwrap(); + + // Tests mapping of `gc -> gc`, `value -> value` + driver.update( + r#" + struct(gc) GcStruct(f64, f64); + struct(value) ValueStruct(f64, f64); + + struct(gc) GcWrapper(GcStruct, ValueStruct) + struct(value) ValueWrapper(GcStruct, ValueStruct); + "#, + ); + + let gc_0 = gc_wrapper.get::("0").unwrap(); + assert_eq!(gc_0.get::("0"), Ok(a.into())); + assert_eq!(gc_0.get::("1"), Ok(b.into())); + + let gc_1 = gc_wrapper.get::("1").unwrap(); + assert_eq!(gc_1.get::("0"), Ok(a.into())); + assert_eq!(gc_1.get::("1"), Ok(b.into())); + + let value_0 = value_wrapper.get::("0").unwrap(); + assert_eq!(value_0.get::("0"), Ok(a.into())); + assert_eq!(value_0.get::("1"), Ok(b.into())); + + let value_1 = value_wrapper.get::("1").unwrap(); + assert_eq!(value_1.get::("0"), Ok(a.into())); + assert_eq!(value_1.get::("1"), Ok(b.into())); + + // Tests an identity mapping + driver.update( + r#" + struct(gc) GcStruct(f64, f64); + struct(value) ValueStruct(f64, f64); + + struct(gc) GcWrapper(GcStruct, ValueStruct) + struct(value) ValueWrapper(GcStruct, ValueStruct); + "#, + ); + + let gc_0 = gc_wrapper.get::("0").unwrap(); + assert_eq!(gc_0.get::("0"), Ok(a.into())); + assert_eq!(gc_0.get::("1"), Ok(b.into())); + + let gc_1 = gc_wrapper.get::("1").unwrap(); + assert_eq!(gc_1.get::("0"), Ok(a.into())); + assert_eq!(gc_1.get::("1"), Ok(b.into())); + + let value_0 = value_wrapper.get::("0").unwrap(); + assert_eq!(value_0.get::("0"), Ok(a.into())); + assert_eq!(value_0.get::("1"), Ok(b.into())); + + let value_1 = value_wrapper.get::("1").unwrap(); + assert_eq!(value_1.get::("0"), Ok(a.into())); + assert_eq!(value_1.get::("1"), Ok(b.into())); + + // Tests mapping of `gc -> value`, `value -> gc` + driver.update( + r#" + struct(value) GcStruct(f64, f64); + struct(gc) ValueStruct(f64, f64); + + struct(gc) GcWrapper(GcStruct, ValueStruct) + struct(value) ValueWrapper(GcStruct, ValueStruct); + "#, + ); + + assert_eq!(gc_0.get::("0"), Ok(a.into())); + assert_eq!(gc_0.get::("1"), Ok(b.into())); + + assert_eq!(gc_1.get::("0"), Ok(a.into())); + assert_eq!(gc_1.get::("1"), Ok(b.into())); + + assert_eq!(value_0.get::("0"), Ok(a.into())); + assert_eq!(value_0.get::("1"), Ok(b.into())); + + assert_eq!(value_1.get::("0"), Ok(a.into())); + assert_eq!(value_1.get::("1"), Ok(b.into())); + + // Tests mapping of different struct type, when `gc -> value`, `value -> gc`, and + // retention of an old library (due to removal of `GcStruct` and `ValueStruct`) + driver.update( + r#" + struct(gc) GcStruct2(f64); + struct(value) ValueStruct2(f64); + + struct(gc) GcWrapper(GcStruct2, ValueStruct2) + struct(value) ValueWrapper(GcStruct2, ValueStruct2); + "#, + ); + + // Existing, rooted objects should remain untouched + assert_eq!(gc_0.get::("0"), Ok(a.into())); + assert_eq!(gc_0.get::("1"), Ok(b.into())); + + assert_eq!(gc_1.get::("0"), Ok(a.into())); + assert_eq!(gc_1.get::("1"), Ok(b.into())); + + assert_eq!(value_0.get::("0"), Ok(a.into())); + assert_eq!(value_0.get::("1"), Ok(b.into())); + + assert_eq!(value_1.get::("0"), Ok(a.into())); + assert_eq!(value_1.get::("1"), Ok(b.into())); + + // The values in the wrappers should have been updated + let mut gc_0 = gc_wrapper.get::("0").unwrap(); + assert_eq!(gc_0.get::("0"), Ok(0.0)); + gc_0.set::("0", a.into()).unwrap(); + + let mut gc_1 = gc_wrapper.get::("1").unwrap(); + assert_eq!(gc_1.get::("0"), Ok(0.0)); + gc_1.set::("0", a.into()).unwrap(); + + let mut value_0 = value_wrapper.get::("0").unwrap(); + assert_eq!(value_0.get::("0"), Ok(0.0)); + value_0.set::("0", a.into()).unwrap(); + + let mut value_1 = value_wrapper.get::("1").unwrap(); + assert_eq!(value_1.get::("0"), Ok(0.0)); + value_1.set::("0", a.into()).unwrap(); + + // Tests mapping of different struct type, when `gc -> gc`, `value -> value` + driver.update( + r#" + struct(gc) GcStruct(f64, f64); + struct(value) ValueStruct(f64, f64); + + struct(gc) GcWrapper(GcStruct, ValueStruct) + struct(value) ValueWrapper(GcStruct, ValueStruct); + "#, + ); + + // Existing, rooted objects should remain untouched + assert_eq!(gc_0.get::("0"), Ok(a.into())); + assert_eq!(gc_1.get::("0"), Ok(a.into())); + assert_eq!(value_0.get::("0"), Ok(a.into())); + assert_eq!(value_1.get::("0"), Ok(a.into())); + + // The values in the wrappers should have been updated + let gc_0 = gc_wrapper.get::("0").unwrap(); + assert_eq!(gc_0.get::("0"), Ok(0.0)); + assert_eq!(gc_0.get::("1"), Ok(0.0)); + + let gc_1 = gc_wrapper.get::("1").unwrap(); + assert_eq!(gc_1.get::("0"), Ok(0.0)); + assert_eq!(gc_1.get::("1"), Ok(0.0)); + + let value_0 = value_wrapper.get::("0").unwrap(); + assert_eq!(value_0.get::("0"), Ok(0.0)); + assert_eq!(value_0.get::("1"), Ok(0.0)); + + let value_1 = value_wrapper.get::("1").unwrap(); + assert_eq!(value_1.get::("0"), Ok(0.0)); + assert_eq!(value_1.get::("1"), Ok(0.0)); +} From 3e4e451e6e2f3bb0f09996ed9231c30cada3d901 Mon Sep 17 00:00:00 2001 From: Wodann Date: Mon, 11 May 2020 17:04:07 +0200 Subject: [PATCH 6/7] feat(memory): allocate inserted struct(gc) fields --- crates/mun_memory/src/gc/mark_sweep.rs | 320 ++++++++++++++----------- crates/mun_memory/src/mapping.rs | 82 ++++--- crates/mun_runtime/tests/memory.rs | 43 ++++ 3 files changed, 262 insertions(+), 183 deletions(-) diff --git a/crates/mun_memory/src/gc/mark_sweep.rs b/crates/mun_memory/src/gc/mark_sweep.rs index abd475133..28f47f00e 100644 --- a/crates/mun_memory/src/gc/mark_sweep.rs +++ b/crates/mun_memory/src/gc/mark_sweep.rs @@ -288,182 +288,210 @@ where gc: &MarkSweep, new_allocations: &mut Vec>>>, conversions: &HashMap>, - mapping: &[Option>], + mapping: &[FieldMapping], src: NonNull, dest: NonNull, ) where T: TypeDesc + TypeLayout + TypeTrace + Clone + Eq + Hash, O: Observer, { - for map in mapping.iter() { - if let Some(FieldMapping { - old_offset, - new_offset, - action, - }) = map - { - let field_src = { - let mut src = src.as_ptr() as usize; - src += old_offset; - src as *mut u8 - }; - let field_dest = { - let mut dest = dest.as_ptr() as usize; - dest += new_offset; - dest as *mut u8 - }; - match action { - mapping::Action::Cast { old, new } => { - if let Some(old_memory_kind) = old.memory_kind() { - let new_memory_kind = new.memory_kind().expect( + for FieldMapping { + new_ty, + new_offset, + action, + } in mapping.iter() + { + let field_dest = { + let mut dest = dest.as_ptr() as usize; + dest += new_offset; + dest as *mut u8 + }; + + match action { + mapping::Action::Cast { old_offset, old_ty } => { + let field_src = { + let mut src = src.as_ptr() as usize; + src += old_offset; + src as *mut u8 + }; + + if let Some(old_memory_kind) = old_ty.memory_kind() { + let new_memory_kind = new_ty.memory_kind().expect( "We are dealing with a struct and we do not directly compare fundamental types and struct type, so its counterpart must also be a struct.", ); - // When the name is the same, we are dealing with the same struct, - // but different internals - let is_same_struct = old.name() == new.name(); - - // If the same struct changed, there must also be a conversion - let conversion = conversions.get(old); - - if old_memory_kind == abi::StructMemoryKind::Value { - if new_memory_kind == abi::StructMemoryKind::Value { - // struct(value) -> struct(value) - if is_same_struct { - // Map in-memory struct to in-memory struct - map_fields( - gc, - new_allocations, - conversions, - &conversion.as_ref().unwrap().field_mapping, - unsafe { NonNull::new_unchecked(field_src) }, - unsafe { NonNull::new_unchecked(field_dest) }, - ); - } else { - // Use previously zero-initialized memory - } - } else { - // struct(value) -> struct(gc) - let object = alloc_obj(new.clone()); - - // We want to return a pointer to the `ObjectInfo`, to be used as handle. - let handle = (object.as_ref().deref() as *const _ - as RawGcPtr) - .into(); - - if is_same_struct { - // Map in-memory struct to heap-allocated struct - map_fields( - gc, - new_allocations, - conversions, - &conversion.as_ref().unwrap().field_mapping, - unsafe { NonNull::new_unchecked(field_src) }, - unsafe { NonNull::new_unchecked(object.ptr) }, - ); - } else { - // Zero initialize heap-allocated object - unsafe { - std::ptr::write_bytes( - (*object).ptr, - 0, - new.layout().size(), - ) - }; - } - - // Write handle to field - let field_handle = field_dest.cast::(); - unsafe { *field_handle = handle }; - - new_allocations.push(object); - } - } else if new_memory_kind == abi::StructMemoryKind::GC { - // struct(gc) -> struct(gc) - let field_src = field_src.cast::(); - let field_dest = field_dest.cast::(); + // When the name is the same, we are dealing with the same struct, + // but different internals + let is_same_struct = old_ty.name() == new_ty.name(); + // If the same struct changed, there must also be a conversion + let conversion = conversions.get(old_ty); + + if old_memory_kind == abi::StructMemoryKind::Value { + if new_memory_kind == abi::StructMemoryKind::Value { + // struct(value) -> struct(value) if is_same_struct { - // Only copy the `GcPtr`. Memory will already be mapped. - unsafe { - *field_dest = *field_src; - } + // Map in-memory struct to in-memory struct + map_fields( + gc, + new_allocations, + conversions, + &conversion.as_ref().unwrap().field_mapping, + unsafe { NonNull::new_unchecked(field_src) }, + unsafe { NonNull::new_unchecked(field_dest) }, + ); } else { - let object = alloc_obj(new.clone()); + // Use previously zero-initialized memory + } + } else { + // struct(value) -> struct(gc) + let object = alloc_obj(new_ty.clone()); - // We want to return a pointer to the `ObjectInfo`, to - // be used as handle. - let handle = (object.as_ref().deref() as *const _ - as RawGcPtr) - .into(); + // We want to return a pointer to the `ObjectInfo`, to be used as handle. + let handle = + (object.as_ref().deref() as *const _ as RawGcPtr).into(); - // Zero-initialize heap-allocated object + if is_same_struct { + // Map in-memory struct to heap-allocated struct + map_fields( + gc, + new_allocations, + conversions, + &conversion.as_ref().unwrap().field_mapping, + unsafe { NonNull::new_unchecked(field_src) }, + unsafe { NonNull::new_unchecked(object.ptr) }, + ); + } else { + // Zero initialize heap-allocated object unsafe { std::ptr::write_bytes( - object.ptr, + (*object).ptr, 0, - new.layout().size(), + new_ty.layout().size(), ) }; + } - // Write handle to field - unsafe { - *field_dest = handle; - } + // Write handle to field + let field_handle = field_dest.cast::(); + unsafe { *field_handle = handle }; - new_allocations.push(object); + new_allocations.push(object); + } + } else if new_memory_kind == abi::StructMemoryKind::GC { + // struct(gc) -> struct(gc) + let field_src = field_src.cast::(); + let field_dest = field_dest.cast::(); + + if is_same_struct { + // Only copy the `GcPtr`. Memory will already be mapped. + unsafe { + *field_dest = *field_src; } } else { - // struct(gc) -> struct(value) - let field_handle = unsafe { *field_src.cast::() }; + let object = alloc_obj(new_ty.clone()); - // Convert the handle to our internal representation - // Safety: we already hold a write lock on `objects`, so - // this is legal. - let obj: *mut ObjectInfo = field_handle.into(); - let obj = unsafe { &*obj }; + // We want to return a pointer to the `ObjectInfo`, to + // be used as handle. + let handle = + (object.as_ref().deref() as *const _ as RawGcPtr).into(); - if is_same_struct { - if obj.ty == *old { - // The object still needs to be mapped - // Map heap-allocated struct to in-memory struct - map_fields( - gc, - new_allocations, - conversions, - &conversion.as_ref().unwrap().field_mapping, - unsafe { NonNull::new_unchecked(obj.ptr) }, - unsafe { NonNull::new_unchecked(field_dest) }, - ); - } else { - // The object was already mapped - debug_assert!(obj.ty == *new); - - // Copy from heap-allocated struct to in-memory struct - unsafe { - std::ptr::copy_nonoverlapping( - obj.ptr, - field_dest, - obj.ty.layout().size(), - ) - }; - } + // Zero-initialize heap-allocated object + unsafe { + std::ptr::write_bytes(object.ptr, 0, new_ty.layout().size()) + }; + + // Write handle to field + unsafe { + *field_dest = handle; + } + + new_allocations.push(object); + } + } else { + // struct(gc) -> struct(value) + let field_handle = unsafe { *field_src.cast::() }; + + // Convert the handle to our internal representation + // Safety: we already hold a write lock on `objects`, so + // this is legal. + let obj: *mut ObjectInfo = field_handle.into(); + let obj = unsafe { &*obj }; + + if is_same_struct { + if obj.ty == *old_ty { + // The object still needs to be mapped + // Map heap-allocated struct to in-memory struct + map_fields( + gc, + new_allocations, + conversions, + &conversion.as_ref().unwrap().field_mapping, + unsafe { NonNull::new_unchecked(obj.ptr) }, + unsafe { NonNull::new_unchecked(field_dest) }, + ); } else { - // Use previously zero-initialized memory + // The object was already mapped + debug_assert!(obj.ty == *new_ty); + + // Copy from heap-allocated struct to in-memory struct + unsafe { + std::ptr::copy_nonoverlapping( + obj.ptr, + field_dest, + obj.ty.layout().size(), + ) + }; } + } else { + // Use previously zero-initialized memory } - } else if !cast::try_cast_from_to( - *old.guid(), - *new.guid(), - unsafe { NonNull::new_unchecked(field_src) }, - unsafe { NonNull::new_unchecked(field_dest) }, - ) { - // Failed to cast. Use the previously zero-initialized value instead } + } else if !cast::try_cast_from_to( + *old_ty.guid(), + *new_ty.guid(), + unsafe { NonNull::new_unchecked(field_src) }, + unsafe { NonNull::new_unchecked(field_dest) }, + ) { + // Failed to cast. Use the previously zero-initialized value instead + } + } + mapping::Action::Copy { old_offset } => { + let field_src = { + let mut src = src.as_ptr() as usize; + src += old_offset; + src as *mut u8 + }; + + unsafe { + std::ptr::copy_nonoverlapping( + field_src, + field_dest, + new_ty.layout().size(), + ) + }; + } + mapping::Action::Insert => { + if let Some(abi::StructMemoryKind::GC) = new_ty.memory_kind() { + let object = alloc_obj(new_ty.clone()); + + // We want to return a pointer to the `ObjectInfo`, to be used as + // handle. + let handle = (object.as_ref().deref() as *const _ as RawGcPtr).into(); + + // Zero-initialize heap-allocated object + unsafe { std::ptr::write_bytes(object.ptr, 0, new_ty.layout().size()) }; + + // Write handle to field + let field_dest = field_dest.cast::(); + unsafe { + *field_dest = handle; + } + + new_allocations.push(object); + } else { + // Use the previously zero-initialized value } - mapping::Action::Copy { size } => unsafe { - std::ptr::copy_nonoverlapping(field_src, field_dest, *size); - }, } } } diff --git a/crates/mun_memory/src/mapping.rs b/crates/mun_memory/src/mapping.rs index b994a4319..d4d06de55 100644 --- a/crates/mun_memory/src/mapping.rs +++ b/crates/mun_memory/src/mapping.rs @@ -15,14 +15,14 @@ pub struct Mapping { } pub struct Conversion { - pub field_mapping: Vec>>, + pub field_mapping: Vec>, pub new_ty: T, } /// Description of the mapping of a single field. When stored together with the new index, this /// provides all information necessary for a mapping function. pub struct FieldMapping { - pub old_offset: usize, + pub new_ty: T, pub new_offset: usize, pub action: Action, } @@ -30,8 +30,9 @@ pub struct FieldMapping { /// The `Action` to take when mapping memory from A to B. #[derive(Eq, PartialEq)] pub enum Action { - Cast { old: T, new: T }, - Copy { size: usize }, + Cast { old_offset: usize, old_ty: T }, + Copy { old_offset: usize }, + Insert, } impl Mapping @@ -155,7 +156,7 @@ pub unsafe fn field_mapping + TypeLayout>( .collect(); struct FieldMappingDesc { - old_index: usize, + old_index: Option, action: ActionDesc, } @@ -163,36 +164,43 @@ pub unsafe fn field_mapping + TypeLayout>( enum ActionDesc { Cast, Copy, + Insert, } // Add mappings for all `old_fields`, unless they were deleted or moved. - let mut mapping: Vec> = (0..old_fields.len()) + let mut mapping: Vec = (0..old_fields.len()) .filter_map(|idx| { if deletions.contains(&idx) { None } else { - Some(Some(FieldMappingDesc { - old_index: idx, + Some(FieldMappingDesc { + old_index: Some(idx), action: ActionDesc::Copy, - })) + }) } }) .collect(); // Sort elements in ascending order of their insertion indices to guarantee that insertions // don't offset "later" insertions. - let mut additions: Vec<(usize, Option)> = diff + let mut additions: Vec<(usize, FieldMappingDesc)> = diff .iter() .filter_map(|diff| match diff { - FieldDiff::Insert { index } => Some((*index, None)), + FieldDiff::Insert { index } => Some(( + *index, + FieldMappingDesc { + old_index: None, + action: ActionDesc::Insert, + }, + )), FieldDiff::Move { old_index, new_index, edit, } => Some(( *new_index, - Some(FieldMappingDesc { - old_index: *old_index, + FieldMappingDesc { + old_index: Some(*old_index), action: edit.as_ref().map_or(ActionDesc::Copy, |kind| { if *kind == FieldEditKind::ConvertType { ActionDesc::Cast @@ -200,7 +208,7 @@ pub unsafe fn field_mapping + TypeLayout>( ActionDesc::Copy } }), - }), + }, )), FieldDiff::Delete { .. } | FieldDiff::Edit { .. } => None, }) @@ -215,13 +223,12 @@ pub unsafe fn field_mapping + TypeLayout>( // Set the action for edited fields. for diff in diff.iter() { if let FieldDiff::Edit { index, kind } = diff { - if let Some(map) = mapping.get_mut(*index).unwrap() { - map.action = if *kind == FieldEditKind::ConvertType { - ActionDesc::Cast - } else { - ActionDesc::Copy - }; - } + let map = mapping.get_mut(*index).unwrap(); + map.action = if *kind == FieldEditKind::ConvertType { + ActionDesc::Cast + } else { + ActionDesc::Copy + }; } } @@ -233,23 +240,24 @@ pub unsafe fn field_mapping + TypeLayout>( .into_iter() .enumerate() .map(|(new_index, desc)| { - desc.map(|desc| { - let old_field = old_fields.get_unchecked(desc.old_index); - FieldMapping { - old_offset: usize::from(*old_offsets.get_unchecked(desc.old_index)), - new_offset: usize::from(*new_offsets.get_unchecked(new_index)), - action: if desc.action == ActionDesc::Cast { - Action::Cast { - old: old_field.1.clone(), - new: new_fields.get_unchecked(new_index).1.clone(), - } - } else { - Action::Copy { - size: old_field.1.layout().size(), - } + let old_offset = desc + .old_index + .map(|idx| usize::from(*old_offsets.get_unchecked(idx))); + + FieldMapping { + new_ty: new_fields.get_unchecked(new_index).1.clone(), + new_offset: usize::from(*new_offsets.get_unchecked(new_index)), + action: match desc.action { + ActionDesc::Cast => Action::Cast { + old_offset: old_offset.unwrap(), + old_ty: old_fields.get_unchecked(desc.old_index.unwrap()).1.clone(), }, - } - }) + ActionDesc::Copy => Action::Copy { + old_offset: old_offset.unwrap(), + }, + ActionDesc::Insert => Action::Insert, + }, + } }) .collect(), new_ty, diff --git a/crates/mun_runtime/tests/memory.rs b/crates/mun_runtime/tests/memory.rs index 736a8d52c..34062e4df 100644 --- a/crates/mun_runtime/tests/memory.rs +++ b/crates/mun_runtime/tests/memory.rs @@ -736,3 +736,46 @@ fn nested_structs() { assert_eq!(value_1.get::("0"), Ok(0.0)); assert_eq!(value_1.get::("1"), Ok(0.0)); } + +#[test] +fn insert_struct() { + let mut driver = TestDriver::new( + r#" + struct Foo { + a: i64, + c: f64, + } + + pub fn foo_new(a: i64, c: f64) -> Foo { + Foo { a, c } + } + "#, + ); + + let a = 5i64; + let c = 3.0f64; + let foo: StructRef = invoke_fn!(driver.runtime_mut(), "foo_new", a, c).unwrap(); + + driver.update( + r#" + struct Bar(i64); + struct(value) Baz(f64); + + struct Foo { + a: i64, + b: Bar, + c: f64, + d: Baz, + } + "#, + ); + + assert_eq!(foo.get::("a").unwrap(), a); + assert_eq!(foo.get::("c").unwrap(), c); + + let b = foo.get::("b").unwrap(); + assert_eq!(b.get::("0"), Ok(0)); + + let d = foo.get::("d").unwrap(); + assert_eq!(d.get::("0"), Ok(0.0)); +} From 8a4e9b17ad7aae5930bfa7dd104dec6985a1360c Mon Sep 17 00:00:00 2001 From: Wodann Date: Wed, 13 May 2020 10:36:35 +0200 Subject: [PATCH 7/7] refactor(memory): simplify and rename TypeLayout trait --- crates/mun_memory/src/gc.rs | 4 +- crates/mun_memory/src/gc/mark_sweep.rs | 46 ++++++++++----------- crates/mun_memory/src/gc/root_ptr.rs | 14 +++---- crates/mun_memory/src/lib.rs | 8 ++-- crates/mun_memory/src/mapping.rs | 20 ++++----- crates/mun_memory/tests/diff/util.rs | 8 ++-- crates/mun_memory/tests/gc/util.rs | 6 +-- crates/mun_runtime/src/garbage_collector.rs | 11 +++-- 8 files changed, 60 insertions(+), 57 deletions(-) diff --git a/crates/mun_memory/src/gc.rs b/crates/mun_memory/src/gc.rs index 526447361..90639d9ba 100644 --- a/crates/mun_memory/src/gc.rs +++ b/crates/mun_memory/src/gc.rs @@ -2,7 +2,7 @@ mod mark_sweep; mod ptr; mod root_ptr; -use crate::TypeLayout; +use crate::TypeMemory; use std::marker::PhantomData; pub use mark_sweep::MarkSweep; @@ -24,7 +24,7 @@ pub trait TypeTrace: Send + Sync { } /// An object that can be used to allocate and collect memory. -pub trait GcRuntime: Send + Sync { +pub trait GcRuntime: Send + Sync { /// Allocates an object of the given type returning a GcPtr fn alloc(&self, ty: T) -> GcPtr; diff --git a/crates/mun_memory/src/gc/mark_sweep.rs b/crates/mun_memory/src/gc/mark_sweep.rs index 28f47f00e..7e0d99f48 100644 --- a/crates/mun_memory/src/gc/mark_sweep.rs +++ b/crates/mun_memory/src/gc/mark_sweep.rs @@ -2,7 +2,7 @@ use crate::{ cast, gc::{Event, GcPtr, GcRuntime, Observer, RawGcPtr, Stats, TypeTrace}, mapping::{self, FieldMapping, MemoryMapper}, - TypeDesc, TypeLayout, + TypeDesc, TypeMemory, }; use mapping::{Conversion, Mapping}; use parking_lot::RwLock; @@ -18,7 +18,7 @@ use std::{ #[derive(Debug)] pub struct MarkSweep where - T: TypeLayout + TypeTrace + Clone, + T: TypeMemory + TypeTrace + Clone, O: Observer, { objects: RwLock>>>>, @@ -28,7 +28,7 @@ where impl Default for MarkSweep where - T: TypeLayout + TypeTrace + Clone, + T: TypeMemory + TypeTrace + Clone, O: Observer + Default, { fn default() -> Self { @@ -42,7 +42,7 @@ where impl MarkSweep where - T: TypeLayout + TypeTrace + Clone, + T: TypeMemory + TypeTrace + Clone, O: Observer, { /// Creates a `MarkSweep` memory collector with the specified `Observer`. @@ -70,7 +70,7 @@ where } } -fn alloc_obj(ty: T) -> Pin>> { +fn alloc_obj(ty: T) -> Pin>> { let ptr = unsafe { std::alloc::alloc(ty.layout()) }; Box::pin(ObjectInfo { ptr, @@ -82,7 +82,7 @@ fn alloc_obj(ty: T) -> Pin> impl GcRuntime for MarkSweep where - T: TypeLayout + TypeTrace + Clone, + T: TypeMemory + TypeTrace + Clone, O: Observer, { fn alloc(&self, ty: T) -> GcPtr { @@ -135,7 +135,7 @@ where impl MarkSweep where - T: TypeLayout + TypeTrace + Clone, + T: TypeMemory + TypeTrace + Clone, O: Observer, { /// Collects all memory that is no longer referenced by rooted objects. Returns `true` if memory @@ -207,7 +207,7 @@ where impl MemoryMapper for MarkSweep where - T: TypeDesc + TypeLayout + TypeTrace + Clone + Eq + Hash, + T: TypeDesc + TypeMemory + TypeTrace + Clone + Eq + Hash, O: Observer, { fn map_memory(&self, mapping: Mapping) -> Vec { @@ -292,7 +292,7 @@ where src: NonNull, dest: NonNull, ) where - T: TypeDesc + TypeLayout + TypeTrace + Clone + Eq + Hash, + T: TypeDesc + TypeMemory + TypeTrace + Clone + Eq + Hash, O: Observer, { for FieldMapping { @@ -315,10 +315,8 @@ where src as *mut u8 }; - if let Some(old_memory_kind) = old_ty.memory_kind() { - let new_memory_kind = new_ty.memory_kind().expect( - "We are dealing with a struct and we do not directly compare fundamental types and struct type, so its counterpart must also be a struct.", - ); + if old_ty.group().is_struct() { + debug_assert!(new_ty.group().is_struct()); // When the name is the same, we are dealing with the same struct, // but different internals @@ -327,8 +325,8 @@ where // If the same struct changed, there must also be a conversion let conversion = conversions.get(old_ty); - if old_memory_kind == abi::StructMemoryKind::Value { - if new_memory_kind == abi::StructMemoryKind::Value { + if old_ty.is_stack_allocated() { + if new_ty.is_stack_allocated() { // struct(value) -> struct(value) if is_same_struct { // Map in-memory struct to in-memory struct @@ -378,7 +376,7 @@ where new_allocations.push(object); } - } else if new_memory_kind == abi::StructMemoryKind::GC { + } else if !new_ty.is_stack_allocated() { // struct(gc) -> struct(gc) let field_src = field_src.cast::(); let field_dest = field_dest.cast::(); @@ -472,7 +470,7 @@ where }; } mapping::Action::Insert => { - if let Some(abi::StructMemoryKind::GC) = new_ty.memory_kind() { + if !new_ty.is_stack_allocated() { let object = alloc_obj(new_ty.clone()); // We want to return a pointer to the `ObjectInfo`, to be used as @@ -516,7 +514,7 @@ enum Color { /// meta information. #[derive(Debug)] #[repr(C)] -struct ObjectInfo { +struct ObjectInfo { pub ptr: *mut u8, pub roots: u32, pub color: Color, @@ -524,28 +522,28 @@ struct ObjectInfo { } /// An `ObjectInfo` is thread-safe. -unsafe impl Send for ObjectInfo {} -unsafe impl Sync for ObjectInfo {} +unsafe impl Send for ObjectInfo {} +unsafe impl Sync for ObjectInfo {} -impl Into<*const ObjectInfo> for GcPtr { +impl Into<*const ObjectInfo> for GcPtr { fn into(self) -> *const ObjectInfo { self.as_ptr() as *const ObjectInfo } } -impl Into<*mut ObjectInfo> for GcPtr { +impl Into<*mut ObjectInfo> for GcPtr { fn into(self) -> *mut ObjectInfo { self.as_ptr() as *mut ObjectInfo } } -impl Into for *const ObjectInfo { +impl Into for *const ObjectInfo { fn into(self) -> GcPtr { (self as RawGcPtr).into() } } -impl Into for *mut ObjectInfo { +impl Into for *mut ObjectInfo { fn into(self) -> GcPtr { (self as RawGcPtr).into() } diff --git a/crates/mun_memory/src/gc/root_ptr.rs b/crates/mun_memory/src/gc/root_ptr.rs index af6f16d15..4b47f306f 100644 --- a/crates/mun_memory/src/gc/root_ptr.rs +++ b/crates/mun_memory/src/gc/root_ptr.rs @@ -1,18 +1,18 @@ use crate::{ gc::{GcPtr, GcRuntime, HasIndirectionPtr, TypeTrace}, - TypeLayout, + TypeMemory, }; use std::marker::PhantomData; use std::sync::{Arc, Weak}; /// A `GcPtr` that automatically roots and unroots its internal `GcPtr`. -pub struct GcRootPtr> { +pub struct GcRootPtr> { handle: GcPtr, runtime: Weak, ty: PhantomData, } -impl> Clone for GcRootPtr { +impl> Clone for GcRootPtr { fn clone(&self) -> Self { if let Some(runtime) = self.runtime.upgrade() { runtime.root(self.handle) @@ -25,7 +25,7 @@ impl> Clone for GcRootPtr { } } -impl> GcRootPtr { +impl> GcRootPtr { /// Constructs a new GCRootHandle from a runtime and a handle pub fn new(runtime: &Arc, handle: GcPtr) -> Self { runtime.root(handle); @@ -47,13 +47,13 @@ impl> GcRootPtr { } } -impl> Into for GcRootPtr { +impl> Into for GcRootPtr { fn into(self) -> GcPtr { self.handle } } -impl> Drop for GcRootPtr { +impl> Drop for GcRootPtr { fn drop(&mut self) { if let Some(runtime) = self.runtime.upgrade() { runtime.unroot(self.handle) @@ -61,7 +61,7 @@ impl> Drop for GcRootPtr { } } -impl> HasIndirectionPtr for GcRootPtr { +impl> HasIndirectionPtr for GcRootPtr { unsafe fn deref(&self) -> *const R { self.handle.deref() } diff --git a/crates/mun_memory/src/lib.rs b/crates/mun_memory/src/lib.rs index 8f32b34ad..0f4b3dcf8 100644 --- a/crates/mun_memory/src/lib.rs +++ b/crates/mun_memory/src/lib.rs @@ -20,12 +20,12 @@ pub trait TypeDesc: Send + Sync { fn group(&self) -> abi::TypeGroup; } -/// A trait used to obtain a type's memory layout. -pub trait TypeLayout: Send + Sync { +/// A trait used to obtain a type's memory description. +pub trait TypeMemory: Send + Sync { /// Returns the memory layout of this type. fn layout(&self) -> Layout; - /// Returns the memory kind of this type, if it is a struct. - fn memory_kind(&self) -> Option; + /// Returns whether the memory is stack-allocated. + fn is_stack_allocated(&self) -> bool; } /// A trait used to obtain a type's fields. diff --git a/crates/mun_memory/src/mapping.rs b/crates/mun_memory/src/mapping.rs index d4d06de55..ef96645dd 100644 --- a/crates/mun_memory/src/mapping.rs +++ b/crates/mun_memory/src/mapping.rs @@ -1,27 +1,27 @@ use crate::{ diff::{diff, Diff, FieldDiff, FieldEditKind}, gc::GcPtr, - TypeDesc, TypeFields, TypeLayout, + TypeDesc, TypeFields, TypeMemory, }; use std::{ collections::{HashMap, HashSet}, hash::Hash, }; -pub struct Mapping { +pub struct Mapping { pub deletions: HashSet, pub conversions: HashMap>, pub identical: Vec<(T, T)>, } -pub struct Conversion { +pub struct Conversion { pub field_mapping: Vec>, pub new_ty: T, } /// Description of the mapping of a single field. When stored together with the new index, this /// provides all information necessary for a mapping function. -pub struct FieldMapping { +pub struct FieldMapping { pub new_ty: T, pub new_offset: usize, pub action: Action, @@ -29,7 +29,7 @@ pub struct FieldMapping { /// The `Action` to take when mapping memory from A to B. #[derive(Eq, PartialEq)] -pub enum Action { +pub enum Action { Cast { old_offset: usize, old_ty: T }, Copy { old_offset: usize }, Insert, @@ -37,7 +37,7 @@ pub enum Action { impl Mapping where - T: TypeDesc + TypeFields + TypeLayout + Copy + Eq + Hash, + T: TypeDesc + TypeFields + TypeMemory + Copy + Eq + Hash, { /// pub fn new(old: &[T], new: &[T]) -> Self { @@ -83,7 +83,7 @@ where let mut new_candidates: HashSet = new .iter() // Filter non-struct types - .filter(|ty| ty.memory_kind().is_some()) + .filter(|ty| ty.group().is_struct()) // Filter inserted structs .filter(|ty| !insertions.contains(*ty)) .cloned() @@ -92,7 +92,7 @@ where let mut old_candidates: HashSet = old .iter() // Filter non-struct types - .filter(|ty| ty.memory_kind().is_some()) + .filter(|ty| ty.group().is_struct()) // Filter deleted structs .filter(|ty| !deletions.contains(*ty)) // Filter edited types @@ -139,7 +139,7 @@ where /// # Safety /// /// Expects the `diff` to be based on `old_ty` and `new_ty`. If not, it causes undefined behavior. -pub unsafe fn field_mapping + TypeLayout>( +pub unsafe fn field_mapping + TypeMemory>( old_ty: T, new_ty: T, diff: &[FieldDiff], @@ -265,7 +265,7 @@ pub unsafe fn field_mapping + TypeLayout>( } /// A trait used to map allocated memory using type differences. -pub trait MemoryMapper { +pub trait MemoryMapper { /// Maps its allocated memory using the provided `mapping`. /// /// A `Vec` is returned containing all objects of types that were deleted. The diff --git a/crates/mun_memory/tests/diff/util.rs b/crates/mun_memory/tests/diff/util.rs index 06743107c..5cc2f080e 100644 --- a/crates/mun_memory/tests/diff/util.rs +++ b/crates/mun_memory/tests/diff/util.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] use mun_memory::{ diff::{myers, Diff, FieldDiff, FieldEditKind}, - TypeDesc, TypeFields, TypeLayout, + TypeDesc, TypeFields, TypeMemory, }; use std::alloc::Layout; @@ -106,14 +106,14 @@ impl TypeDesc for &TypeInfo { } } -impl TypeLayout for &TypeInfo { +impl TypeMemory for &TypeInfo { fn layout(&self) -> Layout { self.layout } - fn memory_kind(&self) -> Option { + fn is_stack_allocated(&self) -> bool { // NOTE: This contrived test does not support structs - None + true } } diff --git a/crates/mun_memory/tests/gc/util.rs b/crates/mun_memory/tests/gc/util.rs index 065ae7d82..769518ca3 100644 --- a/crates/mun_memory/tests/gc/util.rs +++ b/crates/mun_memory/tests/gc/util.rs @@ -72,15 +72,15 @@ macro_rules! impl_struct_ty { impl_primitive_types!(i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, bool); -impl mun_memory::TypeLayout for &'static TypeInfo { +impl mun_memory::TypeMemory for &'static TypeInfo { fn layout(&self) -> Layout { Layout::from_size_align(self.size as usize, self.alignment as usize) .expect("invalid layout specified by TypeInfo") } - fn memory_kind(&self) -> Option { + fn is_stack_allocated(&self) -> bool { // NOTE: This contrived test does not support structs - None + true } } diff --git a/crates/mun_runtime/src/garbage_collector.rs b/crates/mun_runtime/src/garbage_collector.rs index 42f8af634..53986a45d 100644 --- a/crates/mun_runtime/src/garbage_collector.rs +++ b/crates/mun_runtime/src/garbage_collector.rs @@ -113,15 +113,20 @@ impl Iterator for Trace { } } -impl memory::TypeLayout for UnsafeTypeInfo { +impl memory::TypeMemory for UnsafeTypeInfo { fn layout(&self) -> Layout { let ty = unsafe { self.0.as_ref() }; Layout::from_size_align(ty.size_in_bytes(), ty.alignment()) .unwrap_or_else(|_| panic!("invalid layout from Mun Type: {:?}", ty)) } - fn memory_kind(&self) -> Option { - unsafe { self.0.as_ref().as_struct().map(|s| s.memory_kind.clone()) } + fn is_stack_allocated(&self) -> bool { + unsafe { + self.0 + .as_ref() + .as_struct() + .map_or(true, |s| s.memory_kind == abi::StructMemoryKind::Value) + } } }