feat: implement FBIP (Functional But In-Place) reuse analysis

When rc=1 at update sites, mutate in-place instead of allocating new:

List.reverse:
- Swap element pointers in-place instead of creating new list

List.take:
- Truncate list in-place, decref dropped elements

List.drop:
- Shift elements to front in-place, decref dropped elements

List.map:
- Mutate elements in-place, decref old values before storing new

List.filter:
- Filter in-place by shifting kept elements, decref filtered-out elements

All operations check LUX_RC_HEADER(list)->rc == 1 at runtime and
fall back to allocation when rc > 1 (list is shared).

This completes Phase B performance optimizations:
- B1: Last-use optimization (ownership transfer) 
- B2: Reuse analysis (FBIP) 
- B3: Drop specialization 

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-14 14:37:15 -05:00
parent 3a22ae089f
commit 4d5a975b79
2 changed files with 104 additions and 13 deletions

View File

@@ -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));