diff --git a/src/parser.c b/src/parser.c index d746e9c9..3c2d0f44 100644 --- a/src/parser.c +++ b/src/parser.c @@ -2197,6 +2197,307 @@ bool is_logical(opcode_t op) return op == OP_log_and || op == OP_log_or; } +/* Helper function to calculate element size for pointer operations */ +int get_pointer_element_size(var_t *ptr_var) +{ + int element_size = PTR_SIZE; /* Default to pointer size */ + + if (!ptr_var || !ptr_var->type) + return element_size; + + /* Direct pointer with type info */ + if (ptr_var->ptr_level && ptr_var->type) + return ptr_var->type->size; + + /* Typedef pointer or array-derived pointer */ + if (ptr_var->type && ptr_var->type->ptr_level > 0) { + switch (ptr_var->type->base_type) { + case TYPE_char: + return TY_char->size; + case TYPE_int: + return TY_int->size; + case TYPE_void: + return 1; + default: + return ptr_var->type->size ? ptr_var->type->size : PTR_SIZE; + } + } + + /* Array-derived pointer without ptr_level set */ + if (ptr_var->type) { + switch (ptr_var->type->base_type) { + case TYPE_char: + return TY_char->size; + case TYPE_int: + return TY_int->size; + case TYPE_void: + return 1; + default: + return ptr_var->type->size ? ptr_var->type->size : PTR_SIZE; + } + } + + return element_size; +} + +/* Helper function to handle pointer difference calculation */ +void handle_pointer_difference(block_t *parent, + basic_block_t **bb, + var_t *rs1, + var_t *rs2) +{ + /* First perform the subtraction to get byte difference */ + var_t *vd = require_var(parent); + gen_name_to(vd->var_name); + add_insn(parent, *bb, OP_sub, vd, rs1, rs2, 0, NULL); + + /* Determine element size for division */ + int element_size = get_pointer_element_size(rs1); + + /* Divide by element size to get element count */ + if (element_size > 1) { + var_t *size_const = require_var(parent); + gen_name_to(size_const->var_name); + size_const->init_val = element_size; + add_insn(parent, *bb, OP_load_constant, size_const, NULL, NULL, 0, + NULL); + + var_t *result = require_var(parent); + gen_name_to(result->var_name); + add_insn(parent, *bb, OP_div, result, vd, size_const, 0, NULL); + + /* Push the result */ + opstack_push(result); + } else { + /* Element size is 1 (e.g., char), no division needed */ + opstack_push(vd); + } +} + +/* Helper function to handle pointer arithmetic (add/sub with scaling) */ +void handle_pointer_arithmetic(block_t *parent, + basic_block_t **bb, + opcode_t op, + var_t *rs1, + var_t *rs2) +{ + var_t *ptr_var = NULL; + var_t *int_var = NULL; + int element_size = 0; + + /* FIXME: Integer pointer differences are not fully supported. + * The type information needed to determine element size is lost + * when pointer variables are loaded for use in expressions. + * Character pointer differences work because element size is 1. + * + * Current workaround: Cast to char* and divide by sizeof(type) + * Example: ((char*)q - (char*)p) / sizeof(int) + * + * Attempted fixes include looking up original variable declarations, + * but the fundamental issue remains in the compilation pipeline. + */ + + /* Check if both operands are pointers (pointer difference) */ + if (op == OP_sub) { + /* If both are variables (not temporaries), look them up */ + var_t *orig_rs1 = rs1, *orig_rs2 = rs2; + + /* If they have names, they might be variable references - look them up + */ + if (rs1->var_name[0] && !rs1->init_val) { + var_t *found = find_var(rs1->var_name, parent); + if (found) + orig_rs1 = found; + } + if (rs2->var_name[0] && !rs2->init_val) { + var_t *found = find_var(rs2->var_name, parent); + if (found) + orig_rs2 = found; + } + + /* Check if both have ptr_level or typedef pointer type */ + bool rs1_is_ptr = (orig_rs1->ptr_level > 0) || + (orig_rs1->type && orig_rs1->type->ptr_level > 0); + bool rs2_is_ptr = (orig_rs2->ptr_level > 0) || + (orig_rs2->type && orig_rs2->type->ptr_level > 0); + + if (rs1_is_ptr && rs2_is_ptr) { + /* Both are pointers - this is pointer difference */ + /* Determine element size */ + element_size = PTR_SIZE; /* Default */ + + /* Get element size from the first pointer */ + if (orig_rs1->type) { + /* Check if this is a typedef pointer or regular pointer */ + if (orig_rs1->type->ptr_level > 0) { + /* Typedef pointer - element size from base type */ + switch (orig_rs1->type->base_type) { + case TYPE_char: + element_size = 1; + break; + case TYPE_int: + element_size = 4; + break; + default: + /* For struct/union typedef pointers, use the actual + * type size + */ + if (orig_rs1->type->size > 0) + element_size = orig_rs1->type->size; + break; + } + } else if (orig_rs1->ptr_level > 0) { + /* Regular pointer (e.g., int *p) - type gives the base type + */ + switch (orig_rs1->type->base_type) { + case TYPE_char: + element_size = 1; + break; + case TYPE_int: + element_size = 4; + break; + case TYPE_void: + element_size = 1; /* void* arithmetic uses byte size */ + break; + default: + /* For struct pointers, use the struct size */ + element_size = orig_rs1->type->size; + break; + } + } + } + + /* Perform subtraction first */ + var_t *diff = require_var(parent); + gen_name_to(diff->var_name); + add_insn(parent, *bb, OP_sub, diff, rs1, rs2, 0, NULL); + + /* Then divide by element size if needed */ + if (element_size > 1) { + var_t *size_const = require_var(parent); + gen_name_to(size_const->var_name); + size_const->init_val = element_size; + add_insn(parent, *bb, OP_load_constant, size_const, NULL, NULL, + 0, NULL); + + var_t *result = require_var(parent); + gen_name_to(result->var_name); + add_insn(parent, *bb, OP_div, result, diff, size_const, 0, + NULL); + opstack_push(result); + } else { + opstack_push(diff); + } + return; + } + } + /* Determine which operand is the pointer for regular pointer arithmetic */ + if (rs1->ptr_level || (rs1->type && rs1->type->ptr_level > 0)) { + ptr_var = rs1; + int_var = rs2; + element_size = get_pointer_element_size(rs1); + } else if (rs2->ptr_level || (rs2->type && rs2->type->ptr_level > 0)) { + /* Only for addition (p + n == n + p) */ + if (op == OP_add) { + ptr_var = rs2; + int_var = rs1; + element_size = get_pointer_element_size(rs2); + /* Swap operands so pointer is rs1 */ + rs1 = ptr_var; + rs2 = int_var; + } + } + + /* If we need to scale the integer operand */ + if (ptr_var && element_size > 1) { + /* Create multiplication by element size */ + var_t *size_const = require_var(parent); + gen_name_to(size_const->var_name); + size_const->init_val = element_size; + add_insn(parent, *bb, OP_load_constant, size_const, NULL, NULL, 0, + NULL); + + var_t *scaled = require_var(parent); + gen_name_to(scaled->var_name); + add_insn(parent, *bb, OP_mul, scaled, int_var, size_const, 0, NULL); + + /* Use scaled value as rs2 */ + rs2 = scaled; + } + + /* Perform the operation */ + var_t *vd = require_var(parent); + gen_name_to(vd->var_name); + opstack_push(vd); + add_insn(parent, *bb, op, vd, rs1, rs2, 0, NULL); +} + +/* Helper function to check if pointer arithmetic is needed */ +bool is_pointer_operation(opcode_t op, var_t *rs1, var_t *rs2) +{ + if (op != OP_add && op != OP_sub) + return false; + + return (rs1->ptr_level || (rs1->type && rs1->type->ptr_level > 0) || + rs2->ptr_level || (rs2->type && rs2->type->ptr_level > 0)); +} + +/* Helper function to check if a variable is a pointer based on its declaration + */ +bool is_pointer_var(var_t *v, block_t *parent) +{ + if (!v || !v->var_name[0]) + return false; + + /* Check if it has explicit ptr_level or type with ptr_level */ + if (v->ptr_level > 0 || (v->type && v->type->ptr_level > 0)) + return true; + + /* For variables that lost their type info during loading, + * try to find the original declaration */ + var_t *orig = find_var(v->var_name, parent); + if (orig && + (orig->ptr_level > 0 || (orig->type && orig->type->ptr_level > 0))) + return true; + + return false; +} + +/* Helper function to check if it's a pointer difference operation */ +bool is_pointer_difference(opcode_t op, var_t *rs1, var_t *rs2) +{ + if (op != OP_sub) + return false; + + /* Check if both operands are pointers or have pointer types */ + bool rs1_is_ptr = + rs1->ptr_level > 0 || (rs1->type && rs1->type->ptr_level > 0); + bool rs2_is_ptr = + rs2->ptr_level > 0 || (rs2->type && rs2->type->ptr_level > 0); + + /* If both explicitly marked as pointers, it's pointer difference */ + if (rs1_is_ptr && rs2_is_ptr) + return true; + + /* If both variables have the same type and that type has base_type set + * (indicating they're related to typed data), assume it is pointer + * subtraction. + */ + if (rs1->type && rs2->type) { + /* If they have the same type object or same base type, and the + * base type is not void, treat as pointer difference. + */ + if ((rs1->type == rs2->type || + rs1->type->base_type == rs2->type->base_type) && + rs1->type->base_type != TYPE_void && + rs1->type->base_type != TYPE_struct) { + return true; + } + } + + return false; +} + void read_expr(block_t *parent, basic_block_t **bb) { var_t *vd, *rs1, *rs2; @@ -2242,95 +2543,17 @@ void read_expr(block_t *parent, basic_block_t **bb) rs1 = opstack_pop(); /* Handle pointer arithmetic for addition and subtraction */ - if ((top_op == OP_add || top_op == OP_sub) && - (rs1->ptr_level || - (rs1->type && rs1->type->ptr_level > 0) || - rs2->ptr_level || - (rs2->type && rs2->type->ptr_level > 0))) { - var_t *ptr_var = NULL; - var_t *int_var = NULL; - int element_size = 0; - - /* Determine which operand is the pointer */ - if (rs1->ptr_level || - (rs1->type && rs1->type->ptr_level > 0)) { - ptr_var = rs1; - int_var = rs2; - - /* Calculate element size */ - if (rs1->ptr_level && rs1->type) { - element_size = rs1->type->size; - } else if (rs1->type && rs1->type->ptr_level > 0) { - /* Typedef pointer */ - switch (rs1->type->base_type) { - case TYPE_char: - element_size = TY_char->size; - break; - case TYPE_int: - element_size = TY_int->size; - break; - case TYPE_void: - element_size = 1; - break; - default: - element_size = - rs1->type ? rs1->type->size : PTR_SIZE; - break; - } - } - } else if (rs2->ptr_level || - (rs2->type && rs2->type->ptr_level > 0)) { - /* Only for addition (p + n == n + p) */ - if (top_op == OP_add) { - ptr_var = rs2; - int_var = rs1; - - /* Calculate element size */ - if (rs2->ptr_level && rs2->type) { - element_size = rs2->type->size; - } else if (rs2->type && - rs2->type->ptr_level > 0) { - /* Typedef pointer */ - switch (rs2->type->base_type) { - case TYPE_char: - element_size = TY_char->size; - break; - case TYPE_int: - element_size = TY_int->size; - break; - case TYPE_void: - element_size = 1; - break; - default: - element_size = rs2->type - ? rs2->type->size - : PTR_SIZE; - break; - } - } - /* Swap operands so pointer is rs1 */ - rs1 = ptr_var; - rs2 = int_var; - } - } - - /* If we need to scale the integer operand */ - if (ptr_var && element_size > 1) { - /* Create multiplication by element size */ - var_t *size_const = require_var(parent); - gen_name_to(size_const->var_name); - size_const->init_val = element_size; - add_insn(parent, *bb, OP_load_constant, size_const, - NULL, NULL, 0, NULL); - - var_t *scaled = require_var(parent); - gen_name_to(scaled->var_name); - add_insn(parent, *bb, OP_mul, scaled, int_var, - size_const, 0, NULL); - - /* Use scaled value as rs2 */ - rs2 = scaled; + if (is_pointer_operation(top_op, rs1, rs2)) { + if (is_pointer_difference(top_op, rs1, rs2)) { + /* Special case: pointer - pointer difference */ + handle_pointer_difference(parent, bb, rs1, rs2); + } else { + /* Regular pointer arithmetic with scaling */ + handle_pointer_arithmetic(parent, bb, top_op, rs1, + rs2); } + oper_stack_idx--; + continue; } vd = require_var(parent); diff --git a/tests/driver.sh b/tests/driver.sh index 56284c49..a8b33d54 100755 --- a/tests/driver.sh +++ b/tests/driver.sh @@ -1270,6 +1270,136 @@ int main() { } EOF +# Pointer difference calculations +# Test basic pointer subtraction returning element count +try_ 5 << EOF +int main() { + char arr[10]; + char *p = arr; + char *q = arr + 5; + int diff = q - p; /* Should return 5 (5 elements) */ + return diff; +} +EOF + +try_ 3 << EOF +int main() { + char str[20]; + char *start = str + 2; + char *end = str + 5; + return end - start; /* Should return 3 */ +} +EOF + +# Test pointer difference with char pointers (element size = 1) +try_ 7 << EOF +int main() { + char buffer[100]; + char *p1 = buffer; + char *p2 = buffer + 7; + return p2 - p1; /* Should return 7 */ +} +EOF + +# Test reverse pointer difference +try_ 5 << EOF +int main() { + char data[50]; + char *high = data + 10; + char *low = data + 5; + return high - low; /* Should return 5 */ +} +EOF + +# Character pointer differences work correctly because element size is 1. +# Integer and other typed pointer differences face challenges due to type +# information loss during the compilation pipeline. +# +# FIXME: when pointer variables are used in expressions, they become temporaries +# without sufficient type information for proper scaling. +# workaround: For integer pointer differences, cast to char* and divide manually +try_ 7 << EOF +int main() { + int arr[10]; + int *p = arr; + int *q = arr + 7; + /* Workaround: cast to char* and divide by sizeof(int) */ + return ((char*)q - (char*)p) / sizeof(int); /* Returns 7 */ +} +EOF + +# Char pointer differences +try_ 10 << EOF +int main() { + char text[50]; + char *start = text; + char *end = text + 10; + return end - start; /* char pointers work correctly */ +} +EOF + +try_ 0 << EOF +int main() { + char buffer[100]; + char *p1 = buffer + 25; + char *p2 = buffer + 25; + return p2 - p1; /* Same position = 0 */ +} +EOF + +# More complex char pointer arithmetic +try_ 15 << EOF +int main() { + char str[100]; + char *p = str + 5; + char *q = str + 20; + return q - p; /* 20 - 5 = 15 */ +} +EOF + +# Test with void* cast (treated as char*) +try_ 8 << EOF +int main() { + char array[20]; + void *vp1 = array; + void *vp2 = array + 8; + return (char*)vp2 - (char*)vp1; +} +EOF + +# Additional integer pointer +try_ 3 << EOF +int main() { + int nums[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + int *first = &nums[2]; + int *second = &nums[5]; + /* Workaround using char* cast */ + return ((char*)second - (char*)first) / sizeof(int); /* (5-2) = 3 */ +} +EOF + +try_ 10 << EOF +int main() { + int values[20]; + int *p = values; + int *q = values + 10; + /* Another workaround approach */ + return ((char*)q - (char*)p) / sizeof(int); +} +EOF + +# Test negative pointer difference (converted to exit code) +try_ 253 << EOF +int main() { + char data[20]; + char *high = data + 5; + char *low = data + 8; + int diff = high - low; /* -3 */ + /* Convert negative to positive for exit code */ + return diff < 0 ? 256 + diff : diff; /* Returns 253 (256-3) */ +} +EOF + # Category: Function Pointers begin_category "Function Pointers" "Testing function pointer declarations and calls"