aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--codegen.c77
-rw-r--r--tests/072.sh87
2 files changed, 148 insertions, 16 deletions
diff --git a/codegen.c b/codegen.c
index 5b6f4c8..1ee007a 100644
--- a/codegen.c
+++ b/codegen.c
@@ -87,22 +87,39 @@ void codegen_ref_expr(CodeGen* g, AstNode* ast, GenMode gen_mode) {
codegen_expr(g, ast->node_operand, GenMode_lval);
}
+// "reg" stores the address of the expression to be pushed.
+void codegen_push_expr(const char* reg, int size) {
+ if (size == 1) {
+ printf(" movsx %s, BYTE PTR [%s]\n", reg, reg);
+ printf(" push %s\n", reg);
+ } else if (size == 2) {
+ unimplemented();
+ } else if (size == 4) {
+ printf(" movsxd %s, DWORD PTR [%s]\n", reg, reg);
+ printf(" push %s\n", reg);
+ } else if (size == 8) {
+ printf(" mov %s, [%s]\n", reg, reg);
+ printf(" push %s\n", reg);
+ } else {
+ // Perform bitwise copy. Use r10 register as temporary space.
+ // Note: rsp must be aligned to 8.
+ printf(" sub rsp, %d\n", to_aligned(size, 8));
+ int i;
+ for (i = 0; i < size; ++i) {
+ // Copy a sinle byte from the address that "reg" points to to the stack via r10 register.
+ printf(" mov r10b, [%s+%d]\n", reg, i);
+ printf(" mov [rsp+%d], r10b\n", i);
+ }
+ }
+}
+
void codegen_lval2rval(Type* ty) {
if (ty->kind == TypeKind_array) {
return;
}
- int size = type_sizeof(ty);
-
printf(" pop rax\n");
- if (size == 1) {
- printf(" movsx rax, BYTE PTR [rax]\n");
- } else if (size == 4) {
- printf(" movsxd rax, DWORD PTR [rax]\n");
- } else {
- printf(" mov rax, [rax]\n");
- }
- printf(" push rax\n");
+ codegen_push_expr("rax", type_sizeof(ty));
}
void codegen_deref_expr(CodeGen* g, AstNode* ast, GenMode gen_mode) {
@@ -181,6 +198,9 @@ void codegen_binary_expr(CodeGen* g, AstNode* ast, GenMode gen_mode) {
}
void codegen_assign_expr(CodeGen* g, AstNode* ast) {
+ int sizeof_lhs = type_sizeof(ast->node_lhs->ty);
+ int sizeof_rhs = type_sizeof(ast->node_rhs->ty);
+
codegen_expr(g, ast->node_lhs, GenMode_lval);
codegen_expr(g, ast->node_rhs, GenMode_rval);
if (ast->node_op == TokenKind_assign) {
@@ -201,16 +221,40 @@ void codegen_assign_expr(CodeGen* g, AstNode* ast) {
} else {
unreachable();
}
- printf(" pop rdi\n");
- printf(" pop rax\n");
- if (type_sizeof(ast->node_lhs->ty) == 1) {
+ if (sizeof_lhs == 1) {
+ printf(" pop rdi\n");
+ printf(" pop rax\n");
printf(" mov BYTE PTR [rax], dil\n");
- } else if (type_sizeof(ast->node_lhs->ty) == 4) {
+ printf(" push rdi\n");
+ } else if (sizeof_lhs == 4) {
+ printf(" pop rdi\n");
+ printf(" pop rax\n");
printf(" mov DWORD PTR [rax], edi\n");
- } else {
+ printf(" push rdi\n");
+ } else if (sizeof_lhs == 8) {
+ printf(" pop rdi\n");
+ printf(" pop rax\n");
printf(" mov [rax], rdi\n");
+ printf(" push rdi\n");
+ } else {
+ if (ast->node_op != TokenKind_assign) {
+ unimplemented();
+ }
+ // Note: rsp must be aligned to 8.
+ int aligned_size = to_aligned(sizeof_lhs, 8);
+ printf(" mov rax, [rsp+%d]\n", aligned_size);
+ // Perform bitwise copy. Use r10 register as temporary space.
+ int i;
+ for (i = 0; i < aligned_size; ++i) {
+ // Copy a sinle byte from the stack to the address that rax points to via r10 register.
+ printf(" mov r10b, [rsp+%d]\n", i);
+ printf(" mov [rax+%d], r10b\n", i);
+ }
+ // Pop the RHS value and the LHS address.
+ printf(" add rsp, %d\n", aligned_size + 8);
+ // TODO: dummy
+ printf(" push 0\n");
}
- printf(" push rdi\n");
}
void codegen_func_call(CodeGen* g, AstNode* ast) {
@@ -399,6 +443,7 @@ void codegen_continue_stmt(CodeGen* g, AstNode* ast) {
void codegen_expr_stmt(CodeGen* g, AstNode* ast) {
codegen_expr(g, ast->node_expr, GenMode_rval);
+ // TODO: the expression on the stack can be more than 8 bytes.
printf(" pop rax\n");
}
diff --git a/tests/072.sh b/tests/072.sh
new file mode 100644
index 0000000..9e7ab9b
--- /dev/null
+++ b/tests/072.sh
@@ -0,0 +1,87 @@
+set -e
+
+cat <<'EOF' > expected
+123 456
+0 0
+123 456
+EOF
+
+bash ../../test_diff.sh <<'EOF'
+struct S {
+ int a;
+ int b;
+};
+typedef struct S S;
+
+void* calloc();
+int printf();
+
+int main() {
+ S* s1 = calloc(1, sizeof(S));
+ S* s2 = calloc(1, sizeof(S));
+ s1->a = 123;
+ s1->b = 456;
+ printf("%d %d\n", s1->a, s1->b);
+ printf("%d %d\n", s2->a, s2->b);
+ *s2 = *s1;
+ printf("%d %d\n", s2->a, s2->b);
+}
+EOF
+
+cat <<'EOF' > expected
+123 456
+0 0
+123 456
+EOF
+
+bash ../../test_diff.sh <<'EOF'
+struct S {
+ long a;
+ long b;
+};
+typedef struct S S;
+
+void* calloc();
+int printf();
+
+int main() {
+ S* s1 = calloc(1, sizeof(S));
+ S* s2 = calloc(1, sizeof(S));
+ s1->a = 123;
+ s1->b = 456;
+ printf("%d %d\n", s1->a, s1->b);
+ printf("%d %d\n", s2->a, s2->b);
+ *s2 = *s1;
+ printf("%d %d\n", s2->a, s2->b);
+}
+EOF
+
+cat <<'EOF' > expected
+123 456
+0 0
+123 456
+EOF
+
+bash ../../test_diff.sh <<'EOF'
+struct S {
+ long a;
+ long b;
+};
+typedef struct S S;
+
+void* calloc();
+int printf();
+
+int main() {
+ S s1;
+ S s2;
+ s1.a = 123;
+ s1.b = 456;
+ s2.a = 0;
+ s2.b = 0;
+ printf("%d %d\n", s1.a, s1.b);
+ printf("%d %d\n", s2.a, s2.b);
+ s2 = s1;
+ printf("%d %d\n", s2.a, s2.b);
+}
+EOF