Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support pointer data types for sizeof operator #171

Merged
merged 1 commit into from
Dec 15, 2024
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
12 changes: 10 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,18 @@ $(OUT)/tests/%.elf: tests/%.c $(OUT)/$(STAGE0)
chmod +x $@ ; $(PRINTF) "Running $@ ...\n"
$(Q)$(TARGET_EXEC) $@ && $(call pass)

check: $(TESTBINS) tests/driver.sh
tests/driver.sh
check: check-stage0 check-stage2

check-stage0: $(OUT)/$(STAGE0) $(TESTBINS) tests/driver.sh
$(VECHO) " TEST STAGE 0\n"
tests/driver.sh 0

check-stage2: $(OUT)/$(STAGE2) $(TESTBINS) tests/driver.sh
$(VECHO) " TEST STAGE 2\n"
tests/driver.sh 2

check-snapshots: $(OUT)/$(STAGE0) $(SNAPSHOTS) tests/check-snapshots.sh
$(VECHO) " TEST SNAPSHOTS\n"
tests/check-snapshots.sh

$(OUT)/%.o: %.c
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,27 @@ Verify that the emitted IRs are identical to the snapshots by specifying `check-
$ make check-snapshots
```

`shecc` comes with unit tests. To run the tests, give `check` as an argument:
`shecc` comes with unit tests consist of stage 0, stage 2. To run these tests, give `check` as an argument:
```shell
$ make check
```

Reference output:
```
TEST STAGE 0
...
int main(int argc, int argv) { exit(sizeof(char)); } => 1
int main(int argc, int argv) { int a; a = 0; switch (3) { case 0: return 2; case 3: a = 10; break; case 1: return 0; } exit(a); } => 10
int main(int argc, int argv) { int a; a = 0; switch (3) { case 0: return 2; default: a = 10; break; } exit(a); } => 10
OK
TEST STAGE 2
...
int main(int argc, int argv) { exit(sizeof(char*)); }
exit code => 4
output =>
int main(int argc, int argv) { exit(sizeof(int*)); }
exit code => 4
output =>
OK
```

Expand Down
2 changes: 2 additions & 0 deletions lib/c.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#define false 0

#if defined(__arm__)
#define __SIZEOF_POINTER__ 4
#define __syscall_exit 1
#define __syscall_read 3
#define __syscall_write 4
Expand All @@ -23,6 +24,7 @@
#define __syscall_munmap 91

#elif defined(__riscv)
#define __SIZEOF_POINTER__ 4
#define __syscall_exit 93
#define __syscall_read 63
#define __syscall_write 64
Expand Down
9 changes: 8 additions & 1 deletion src/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,11 @@ void read_expr_operand(block_t *parent, basic_block_t **bb)
read_ternary_operation(parent, bb);
lex_expect(T_close_bracket);
} else if (lex_accept(T_sizeof)) {
/* TODO: Use more generalized type grammar parsing function to handle
* type reading
*/
char token[MAX_TYPE_LEN];
int ptr_cnt = 0;

lex_expect(T_open_bracket);
int find_type_flag = lex_accept(T_struct) ? 2 : 1;
Expand All @@ -853,9 +857,12 @@ void read_expr_operand(block_t *parent, basic_block_t **bb)
if (!type)
error("Unable to find type");

while (lex_accept(T_asterisk))
ptr_cnt++;

ph1_ir = add_ph1_ir(OP_load_constant);
vd = require_var(parent);
vd->init_val = type->size;
vd->init_val = ptr_cnt ? PTR_SIZE : type->size;
strcpy(vd->var_name, gen_name());
ph1_ir->dest = vd;
opstack_push(vd);
Expand Down
23 changes: 18 additions & 5 deletions src/reg-alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@
* dead variable and does NOT wrtie it back to the stack.
*/

/* Aligns size to nearest multiple of 4, this meets
* ARMv7's alignment requirement.
*
* This function should
* be called whenever handling with user-defined type's
* size.
*/
int align_size(int i)
{
return i <= 4 ? 4 : (i + 3) & ~3;
}

bool check_live_out(basic_block_t *bb, var_t *var)
{
for (int i = 0; i < bb->live_out_idx; i++) {
Expand Down Expand Up @@ -239,11 +251,11 @@ void reg_alloc()
src0 = GLOBAL_FUNC.stack_size;
if (global_insn->rd->is_ptr)
GLOBAL_FUNC.stack_size +=
(PTR_SIZE * global_insn->rd->array_size);
align_size(PTR_SIZE * global_insn->rd->array_size);
else {
type_t *type = find_type(global_insn->rd->type_name, 0);
GLOBAL_FUNC.stack_size +=
(global_insn->rd->array_size * type->size);
align_size(global_insn->rd->array_size * type->size);
}

dest =
Expand All @@ -260,7 +272,7 @@ void reg_alloc()
strcmp(global_insn->rd->type_name, "char") &&
strcmp(global_insn->rd->type_name, "_Bool")) {
type_t *type = find_type(global_insn->rd->type_name, 0);
GLOBAL_FUNC.stack_size += type->size;
GLOBAL_FUNC.stack_size += align_size(type->size);
} else
/* 'char' is aligned to one byte for the convenience */
GLOBAL_FUNC.stack_size += 4;
Expand Down Expand Up @@ -365,9 +377,10 @@ void reg_alloc()
}

if (insn->rd->array_size)
fn->func->stack_size += (insn->rd->array_size * sz);
fn->func->stack_size +=
align_size(insn->rd->array_size * sz);
else
fn->func->stack_size += sz;
fn->func->stack_size += align_size(sz);

dest = prepare_dest(bb, insn->rd, -1, -1);
ir = bb_add_ph2_ir(bb, OP_address_of);
Expand Down
9 changes: 5 additions & 4 deletions src/riscv-codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ void update_elf_offset(ph2_ir_t *ph2_ir)
if (hard_mul_div)
elf_offset += 4;
else
elf_offset += 104;
elf_offset += 108;
return;
case OP_load_data_address:
case OP_neq:
Expand Down Expand Up @@ -331,12 +331,13 @@ void emit_ph2_ir(ph2_ir_t *ph2_ir)
/* Unsigned integer division */
emit(__addi(__t0, __zero, 0));
emit(__addi(__t1, __zero, 1));
emit(__beq(__t3, __zero, 48));
emit(__beq(__t2, __zero, 44));
emit(__beq(__t3, __zero, 52));
emit(__beq(__t2, __zero, 48));
emit(__beq(__t2, __t3, 20));
emit(__bltu(__t2, __t3, 16));
emit(__slli(__t3, __t3, 1));
emit(__slli(__t1, __t1, 1));
emit(__jal(__zero, -12));
emit(__jal(__zero, -16));
emit(__bltu(__t2, __t3, 12));
emit(__sub(__t2, __t2, __t3));
emit(__add(__t0, __t0, __t1));
Expand Down
47 changes: 45 additions & 2 deletions tests/driver.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,22 @@

set -u

readonly SHECC="$PWD/out/shecc"
if [ "$#" != 1 ]; then
echo "Usage: $0 <stage>"
exit 1
fi

case "$1" in
"0")
readonly SHECC="$PWD/out/shecc" ;;
"1")
readonly SHECC="$PWD/out/shecc-stage1.elf" ;;
"2")
readonly SHECC="$PWD/out/shecc-stage2.elf" ;;
*)
echo "$1 is not a valid stage"
exit 1 ;;
esac

# try - test shecc with given code
# Usage:
Expand Down Expand Up @@ -405,8 +420,36 @@ items 5 "int a; a = 10; a -= 5; return a;"
items 20 "int *p; int a[3]; a[0] = 10; a[1] = 20; a[2] = 30; p = a; p+=1; return p[0];"

# sizeof
expr 4 "sizeof(int)";
expr 0 "sizeof(void)";
expr 1 "sizeof(_Bool)";
expr 1 "sizeof(char)";
expr 4 "sizeof(int)";
# sizeof pointers
expr 4 "sizeof(void*)";
expr 4 "sizeof(_Bool*)";
expr 4 "sizeof(char*)";
expr 4 "sizeof(int*)";
# sizeof multi-level pointer
expr 4 "sizeof(void**)";
expr 4 "sizeof(_Bool**)";
expr 4 "sizeof(char**)";
expr 4 "sizeof(int**)";
# sizeof struct
try_ 4 << EOF
typedef struct {
int a;
int b;
} struct_t;
int main() { return sizeof(struct_t*); }
EOF
# sizeof enum
try_ 4 << EOF
typedef enum {
A,
B
} enum_t;
int main() { return sizeof(enum_t*); }
EOF

# switch-case
items 10 "int a; a = 0; switch (3) { case 0: return 2; case 3: a = 10; break; case 1: return 0; } return a;"
Expand Down