diff --git a/docs/REFERENCE_COUNTING.md b/docs/REFERENCE_COUNTING.md index f1a2a1e..da5a91a 100644 --- a/docs/REFERENCE_COUNTING.md +++ b/docs/REFERENCE_COUNTING.md @@ -33,7 +33,7 @@ The RC system is now functional for lists and boxed values. ``` ### What's NOT Yet Implemented -- Reuse analysis (FBIP) - mutate in-place when rc=1 +- Drop fusion - combining consecutive drops (minor optimization) ## The Problem @@ -345,7 +345,7 @@ void lux_check_leaks() { | Conditionals | Yes | Yes ✅ | | Last-use opt | Yes | Yes ✅ (ownership transfer) | | Drop special | Yes | Yes ✅ | -| Reuse (FBIP) | Yes | No | +| Reuse (FBIP) | Yes | Yes ✅ | | Drop fusion | Yes | No | --- @@ -438,11 +438,12 @@ Rust's ownership system is fundamentally different: - Source variable unregistered from RC tracking - No double-free, no unnecessary incref/decref -2. **Reuse analysis (FBIP)** - NOT YET IMPLEMENTED - - Detect `rc=1` at update sites - - Mutate in-place instead of copy - - Major change to list operations - - ~300 lines +2. ~~**Reuse analysis (FBIP)**~~ ✅ DONE + - Runtime check `LUX_RC_HEADER(list)->rc == 1` + - List.map: mutate elements in-place, decref old values + - List.filter: filter in-place, decref removed elements + - List.reverse: swap pointers in-place + - List.take/drop: truncate/shift in-place 3. ~~**Drop specialization**~~ ✅ DONE - Specialized decref functions: `lux_decref_list`, `lux_decref_closure`, etc. @@ -458,12 +459,13 @@ Rust's ownership system is fundamentally different: | A3 | Early returns | ~30 | P1 | ✅ Done | | A4 | Conditionals | ~50 | P2 | ✅ Done | | B1 | Last-use opt | ~80 | P3 | ✅ Done | -| B2 | Reuse (FBIP) | ~300 | P3 - Performance | Pending | +| B2 | Reuse (FBIP) | ~150 | P3 | ✅ Done | | B3 | Drop special | ~100 | P3 | ✅ Done | **Phase A: COMPLETE** ✅ - All leak prevention implemented -**Phase B: 2/3 DONE** ✅ - Major performance optimizations implemented -**Remaining: FBIP (~300 lines)** - In-place mutation when rc=1 +**Phase B: COMPLETE** ✅ - All major performance optimizations implemented + +Only remaining: Drop fusion (minor optimization, low priority) ### Cycle Detection diff --git a/src/codegen/c_backend.rs b/src/codegen/c_backend.rs index e495d07..fa459a9 100644 --- a/src/codegen/c_backend.rs +++ b/src/codegen/c_backend.rs @@ -1039,6 +1039,18 @@ impl CBackend { self.writeln("}"); self.writeln(""); self.writeln("static LuxList* lux_list_reverse(LuxList* list) {"); + self.writeln(" // FBIP: If rc=1, reverse in-place instead of copying"); + self.writeln(" if (LUX_RC_HEADER(list)->rc == 1) {"); + self.writeln(" // In-place reversal - just swap element pointers"); + self.writeln(" int64_t n = list->length;"); + self.writeln(" for (int64_t i = 0; i < n / 2; i++) {"); + self.writeln(" void* tmp = list->elements[i];"); + self.writeln(" list->elements[i] = list->elements[n - 1 - i];"); + self.writeln(" list->elements[n - 1 - i] = tmp;"); + self.writeln(" }"); + self.writeln(" return list; // Reuse same list"); + self.writeln(" }"); + self.writeln(" // rc > 1: Allocate new list (standard path)"); self.writeln(" LuxList* result = lux_list_new(list->length);"); self.writeln(" for (int64_t i = 0; i < list->length; i++) {"); self.writeln(" lux_incref(list->elements[list->length - 1 - i]);"); @@ -1050,7 +1062,20 @@ impl CBackend { self.writeln(""); self.writeln("static LuxList* lux_list_take(LuxList* list, int64_t n) {"); self.writeln(" if (n <= 0) return lux_list_new(0);"); - self.writeln(" if (n > list->length) n = list->length;"); + self.writeln(" if (n >= list->length) {"); + self.writeln(" lux_incref(list); // Return same list"); + self.writeln(" return list;"); + self.writeln(" }"); + self.writeln(" // FBIP: If rc=1, truncate in-place"); + self.writeln(" if (LUX_RC_HEADER(list)->rc == 1) {"); + self.writeln(" // Decref elements we're dropping"); + self.writeln(" for (int64_t i = n; i < list->length; i++) {"); + self.writeln(" lux_decref(list->elements[i]);"); + self.writeln(" }"); + self.writeln(" list->length = n;"); + self.writeln(" return list; // Reuse same list"); + self.writeln(" }"); + self.writeln(" // rc > 1: Allocate new list"); self.writeln(" LuxList* result = lux_list_new(n);"); self.writeln(" for (int64_t i = 0; i < n; i++) {"); self.writeln(" lux_incref(list->elements[i]);"); @@ -1067,6 +1092,20 @@ impl CBackend { self.writeln(" return list;"); self.writeln(" }"); self.writeln(" int64_t new_len = list->length - n;"); + self.writeln(" // FBIP: If rc=1, shift elements in-place"); + self.writeln(" if (LUX_RC_HEADER(list)->rc == 1) {"); + self.writeln(" // Decref elements we're dropping"); + self.writeln(" for (int64_t i = 0; i < n; i++) {"); + self.writeln(" lux_decref(list->elements[i]);"); + self.writeln(" }"); + self.writeln(" // Shift remaining elements to front"); + self.writeln(" for (int64_t i = 0; i < new_len; i++) {"); + self.writeln(" list->elements[i] = list->elements[n + i];"); + self.writeln(" }"); + self.writeln(" list->length = new_len;"); + self.writeln(" return list; // Reuse same list"); + self.writeln(" }"); + self.writeln(" // rc > 1: Allocate new list"); self.writeln(" LuxList* result = lux_list_new(new_len);"); self.writeln(" for (int64_t i = 0; i < new_len; i++) {"); self.writeln(" lux_incref(list->elements[n + i]);"); @@ -2432,7 +2471,26 @@ impl CBackend { let fn_var = format!("_fn_{}", id); let mapped_var = format!("_mapped_{}", id); - self.writeln(&format!("LuxList* {} = lux_list_new({}->length);", result_var, list)); + // FBIP: Check if we can reuse the list in-place + self.writeln(&format!("LuxList* {};", result_var)); + self.writeln(&format!("if (LUX_RC_HEADER({})->rc == 1) {{", list)); + self.indent += 1; + self.writeln(&format!("// FBIP: Reuse list in-place")); + self.writeln(&format!("{} = {};", result_var, list)); + self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var)); + self.indent += 1; + self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var)); + self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure)); + self.writeln(&format!("LuxInt {} = ((LuxInt(*)(void*, LuxInt)){}->fn_ptr)({}->env, lux_unbox_int({}));", mapped_var, fn_var, fn_var, elem_var)); + self.writeln(&format!("lux_decref({}); // Decref old element", elem_var)); + self.writeln(&format!("{}->elements[{}] = lux_box_int({});", result_var, i_var, mapped_var)); + self.indent -= 1; + self.writeln("}"); + self.indent -= 1; + self.writeln("} else {"); + self.indent += 1; + self.writeln(&format!("// Allocate new list")); + self.writeln(&format!("{} = lux_list_new({}->length);", result_var, list)); self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var)); self.indent += 1; self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var)); @@ -2442,6 +2500,8 @@ impl CBackend { self.indent -= 1; self.writeln("}"); self.writeln(&format!("{}->length = {}->length;", result_var, list)); + self.indent -= 1; + self.writeln("}"); // Decref the closure if it was a temporary (inline lambda) if closure.starts_with("_closure_") { self.writeln(&format!("lux_decref_closure({});", closure)); @@ -2463,8 +2523,35 @@ impl CBackend { let fn_var = format!("_fn_{}", id); let keep_var = format!("_keep_{}", id); - self.writeln(&format!("LuxList* {} = lux_list_new({}->length);", result_var, list)); + // FBIP: Check if we can filter in-place + self.writeln(&format!("LuxList* {};", result_var)); self.writeln(&format!("int64_t {} = 0;", count_var)); + self.writeln(&format!("if (LUX_RC_HEADER({})->rc == 1) {{", list)); + self.indent += 1; + self.writeln(&format!("// FBIP: Filter in-place")); + self.writeln(&format!("{} = {};", result_var, list)); + self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var)); + self.indent += 1; + self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var)); + self.writeln(&format!("LuxClosure* {} = (LuxClosure*){};", fn_var, closure)); + self.writeln(&format!("LuxBool {} = ((LuxBool(*)(void*, LuxInt)){}->fn_ptr)({}->env, lux_unbox_int({}));", keep_var, fn_var, fn_var, elem_var)); + self.writeln(&format!("if ({}) {{", keep_var)); + self.indent += 1; + self.writeln(&format!("{}->elements[{}++] = {};", result_var, count_var, elem_var)); + self.indent -= 1; + self.writeln("} else {"); + self.indent += 1; + self.writeln(&format!("lux_decref({}); // Decref filtered-out element", elem_var)); + self.indent -= 1; + self.writeln("}"); + self.indent -= 1; + self.writeln("}"); + self.writeln(&format!("{}->length = {};", result_var, count_var)); + self.indent -= 1; + self.writeln("} else {"); + self.indent += 1; + self.writeln(&format!("// Allocate new list")); + self.writeln(&format!("{} = lux_list_new({}->length);", result_var, list)); self.writeln(&format!("for (int64_t {} = 0; {} < {}->length; {}++) {{", i_var, i_var, list, i_var)); self.indent += 1; self.writeln(&format!("void* {} = {}->elements[{}];", elem_var, list, i_var)); @@ -2480,6 +2567,8 @@ impl CBackend { self.indent -= 1; self.writeln("}"); self.writeln(&format!("{}->length = {};", result_var, count_var)); + self.indent -= 1; + self.writeln("}"); // Decref the closure if it was a temporary (inline lambda) if closure.starts_with("_closure_") { self.writeln(&format!("lux_decref_closure({});", closure));