Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/arm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
172 changes: 29 additions & 143 deletions src/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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) */
Expand All @@ -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 */
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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)) {
Expand Down
94 changes: 79 additions & 15 deletions tests/driver.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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

Expand Down