diff --git a/src/arm.c b/src/arm.c index 3fae60b7..28422b03 100644 --- a/src/arm.c +++ b/src/arm.c @@ -230,8 +230,12 @@ int __sll_amt(arm_cond_t cond, int __sra(arm_cond_t cond, arm_reg rd, arm_reg rm, arm_reg rs) { + /* Arithmetic right shift with register + * Bit 4 = 1 (register-specified shift) + * Bits 5-6 = arith_rs (2) for arithmetic right shift + */ return arm_encode(cond, 0 + (arm_mov << 1) + (0 << 5), 0, rd, - rm + (5 << 4) + (rs << 8)); + rm + (1 << 4) + (arith_rs << 5) + (rs << 8)); } int __add_i(arm_cond_t cond, arm_reg rd, arm_reg rs, int imm) diff --git a/src/parser.c b/src/parser.c index 3c2d0f44..dc81db92 100644 --- a/src/parser.c +++ b/src/parser.c @@ -2265,7 +2265,6 @@ void handle_pointer_difference(block_t *parent, 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 { @@ -2285,16 +2284,8 @@ void handle_pointer_arithmetic(block_t *parent, 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. + /* Pointer arithmetic: differences (char*, int*, struct*, etc.), + * addition/increment with scaling, and array indexing. */ /* Check if both operands are pointers (pointer difference) */ @@ -2321,6 +2312,16 @@ void handle_pointer_arithmetic(block_t *parent, bool rs2_is_ptr = (orig_rs2->ptr_level > 0) || (orig_rs2->type && orig_rs2->type->ptr_level > 0); + /* If variable lookup failed, check the passed variables directly */ + if (!rs1_is_ptr) { + rs1_is_ptr = + (rs1->ptr_level > 0) || (rs1->type && rs1->type->ptr_level > 0); + } + if (!rs2_is_ptr) { + rs2_is_ptr = + (rs2->ptr_level > 0) || (rs2->type && rs2->type->ptr_level > 0); + } + if (rs1_is_ptr && rs2_is_ptr) { /* Both are pointers - this is pointer difference */ /* Determine element size */ @@ -2427,6 +2428,11 @@ void handle_pointer_arithmetic(block_t *parent, /* Perform the operation */ var_t *vd = require_var(parent); + /* Preserve pointer type metadata on results of pointer arithmetic */ + if (ptr_var) { + vd->type = ptr_var->type; + vd->ptr_level = ptr_var->ptr_level; + } gen_name_to(vd->var_name); opstack_push(vd); add_insn(parent, *bb, op, vd, rs1, rs2, 0, NULL); @@ -2463,41 +2469,6 @@ bool is_pointer_var(var_t *v, block_t *parent) 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; @@ -2544,14 +2515,10 @@ void read_expr(block_t *parent, basic_block_t **bb) /* Handle pointer arithmetic for addition and subtraction */ 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); - } + /* handle_pointer_arithmetic handles both pointer + * differences and regular pointer arithmetic internally + */ + handle_pointer_arithmetic(parent, bb, top_op, rs1, rs2); oper_stack_idx--; continue; } @@ -2671,90 +2638,10 @@ void read_expr(block_t *parent, basic_block_t **bb) rs2 = opstack_pop(); 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; - } + /* Pointer arithmetic handling */ + if (is_pointer_operation(top_op, rs1, rs2)) { + handle_pointer_arithmetic(parent, bb, top_op, rs1, rs2); + continue; /* skip normal processing */ } /* Constant folding for binary operations */ @@ -4496,9 +4383,8 @@ basic_block_t *read_body_statement(block_t *parent, basic_block_t *bb) if (lex_accept(T_assign)) { if (lex_peek(T_open_curly, NULL) && (var->array_size > 0 || var->ptr_level > 0)) { - parse_array_init( - var, parent, &bb, - 1); /* FIXED: Emit code for locals in functions */ + /* Emit code for locals in functions */ + parse_array_init(var, parent, &bb, 1); } else if (lex_peek(T_open_curly, NULL) && (var->type->base_type == TYPE_struct || var->type->base_type == TYPE_typedef)) { @@ -4609,8 +4495,8 @@ basic_block_t *read_body_statement(block_t *parent, basic_block_t *bb) if (lex_accept(T_assign)) { if (lex_peek(T_open_curly, NULL) && (nv->array_size > 0 || nv->ptr_level > 0)) { - parse_array_init(nv, parent, &bb, - 1); /* FIXED: Emit code for locals */ + /* Emit code for locals */ + parse_array_init(nv, parent, &bb, 1); } else if (lex_peek(T_open_curly, NULL) && (nv->type->base_type == TYPE_struct || nv->type->base_type == TYPE_typedef)) { diff --git a/tests/driver.sh b/tests/driver.sh index a8b33d54..814c5b5e 100755 --- a/tests/driver.sh +++ b/tests/driver.sh @@ -1311,20 +1311,15 @@ int main() { } 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 +# Pointer arithmetic tests + +# Basic integer pointer difference 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 */ + return q - p; } EOF @@ -1334,7 +1329,7 @@ int main() { char text[50]; char *start = text; char *end = text + 10; - return end - start; /* char pointers work correctly */ + return end - start; } EOF @@ -1367,24 +1362,93 @@ int main() { } EOF -# Additional integer pointer +# Integer pointer with array indexing 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 */ + return second - first; /* Direct subtraction: (5-2) = 3 */ } EOF +# Larger integer pointer difference 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); + return q - p; /* Direct pointer arithmetic */ +} +EOF + +# Negative pointer difference +try_ 251 << EOF +int main() { + int arr[10]; + int *p = arr + 8; + int *q = arr + 3; + return q - p; /* 3 - 8 = -5, wraps to 251 in exit code */ +} +EOF + +# Zero pointer difference +try_ 0 << EOF +int main() { + int data[10]; + int *p1 = data + 5; + int *p2 = data + 5; + return p2 - p1; /* Same position = 0 */ +} +EOF + +# Struct pointer arithmetic +try_ 4 << EOF +struct point { + int x; + int y; + int z; +}; + +int main() { + struct point pts[10]; + struct point *p1 = pts; + struct point *p2 = pts + 4; + return p2 - p1; /* Struct pointer difference */ +} +EOF + +# Mixed pointer arithmetic operations +try_ 16 << EOF +int main() { + int arr[20]; + int *start = arr; + int *mid = arr + 10; + int *end = arr + 18; + return (end - mid) + (mid - start) - 2; /* (18-10) + (10-0) - 2 = 8 + 10 - 2 = 16 */ +} +EOF + +# Pointer arithmetic with typedef +try_ 6 << EOF +typedef int* int_ptr; +int main() { + int data[15]; + int_ptr p1 = data + 2; + int_ptr p2 = data + 8; + return p2 - p1; /* Typedef pointer difference: 8 - 2 = 6 */ +} +EOF + +# Complex expression with pointer differences +try_ 13 << EOF +int main() { + int vals[30]; + int *a = vals; + int *b = vals + 5; + int *c = vals + 9; + int *d = vals + 15; + return (d - a) - (c - b) + 2; /* (15-0) - (9-5) + 2 = 15 - 4 + 2 = 13 */ } EOF